mirror of
https://github.com/FancyInnovations/FancyPlugins.git
synced 2025-12-06 07:43:36 +00:00
Add FancyHolograms sources
This commit is contained in:
70
plugins/fancyholograms-v2/Jenkinsfile
vendored
Normal file
70
plugins/fancyholograms-v2/Jenkinsfile
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
Required env: java 21, git
|
||||||
|
Required plugins: discord notifier
|
||||||
|
Required credentials: MODRINTH_PUBLISH_API_TOKEN, HANGAR_PUBLISH_API_TOKEN
|
||||||
|
*/
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
GRADLE_OPTS = '-Dorg.gradle.daemon=false'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
git url: 'https://github.com/FancyMcPlugins/fancyplugins', branch: 'main'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh 'chmod +x gradlew'
|
||||||
|
sh './gradlew clean :plugins:fancyholograms-v2:shadowJar'
|
||||||
|
echo 'Built the plugin!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy') {
|
||||||
|
steps {
|
||||||
|
// Load the secrets and make them available as environment variables
|
||||||
|
withCredentials([
|
||||||
|
string(credentialsId: 'MODRINTH_PUBLISH_API_TOKEN', variable: 'MODRINTH_PUBLISH_API_TOKEN'),
|
||||||
|
string(credentialsId: 'HANGAR_PUBLISH_API_TOKEN', variable: 'HANGAR_PUBLISH_API_TOKEN')
|
||||||
|
]) {
|
||||||
|
sh 'export MODRINTH_PUBLISH_API_TOKEN=${MODRINTH_PUBLISH_API_TOKEN} && ./gradlew :plugins:fancyholograms-v2:modrinth'
|
||||||
|
echo 'Published to Modrinth!'
|
||||||
|
|
||||||
|
sh 'export HANGAR_PUBLISH_API_TOKEN=${HANGAR_PUBLISH_API_TOKEN} && ./gradlew :plugins:fancyholograms-v2:publishAllPublicationsToHangar'
|
||||||
|
echo 'Published to Hangar!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
archiveArtifacts artifacts: 'plugins/fancyholograms-v2/build/libs/FancyHolograms-*.jar', allowEmptyArchive: true
|
||||||
|
}
|
||||||
|
success {
|
||||||
|
withCredentials([
|
||||||
|
string(credentialsId: 'DISC_WEBHOOK_URL', variable: 'DISC_WEBHOOK_URL')
|
||||||
|
]) {
|
||||||
|
discordSend description: "**Build:** ${env.BUILD_NUMBER} \n**Status:** ${currentBuild.currentResult} \n**Download:** https://modrinth.com/plugin/fancyholograms/versions",
|
||||||
|
footer: "Jenkins Pipeline", link: env.BUILD_URL, result: 'SUCCESS', title: "FancyHolograms #${env.BUILD_NUMBER}", webhookURL: "${DISC_WEBHOOK_URL}"
|
||||||
|
}
|
||||||
|
echo 'Build was successful!'
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
script {
|
||||||
|
withCredentials([
|
||||||
|
string(credentialsId: 'DISC_WEBHOOK_URL', variable: 'DISC_WEBHOOK_URL')
|
||||||
|
]) {
|
||||||
|
discordSend description: "**Build:** ${env.BUILD_NUMBER} \n**Status:** ${currentBuild.currentResult}", footer: "Jenkins Pipeline", link: env.BUILD_URL, result: 'FAILURE', title: "FancyHolograms #${env.BUILD_NUMBER}", "${DISC_WEBHOOK_URL}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo 'Build failed!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
plugins/fancyholograms-v2/README.md
Normal file
145
plugins/fancyholograms-v2/README.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[](https://github.com/FancyMcPlugins/FancyHolograms/releases/latest)
|
||||||
|
[](https://papermc.io/software/folia)
|
||||||
|
[](https://discord.gg/ZUgYCEJUEx)
|
||||||
|
[](https://github.com/FancyMcPlugins/FancyHolograms/releases/latest)
|
||||||
|
[](https://modrinth.com/plugin/fancyholograms)
|
||||||
|
[](https://www.codefactor.io/repository/github/fancymcplugins/fancyholograms/issues/main)
|
||||||
|
|
||||||
|
[](https://modrinth.com/plugin/fancyholograms)
|
||||||
|
[](https://hangar.papermc.io/Oliver/FancyHolograms)
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Simple, lightweight and feature-rich hologram plugin for **[Paper](https://papermc.io/software/paper)** (
|
||||||
|
and [Folia](https://papermc.io/software/folia)) servers using [display entities](https://minecraft.wiki/w/Display)
|
||||||
|
and packets.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
With this plugin you can create holograms with customizable properties like:
|
||||||
|
|
||||||
|
- **Hologram Type** (text, item or block)
|
||||||
|
- **Position**, **Rotation** and **Scale**
|
||||||
|
- **Text Alignment**, **Background Color** and **Shadow**.
|
||||||
|
- **Billboard** (fixed, center, horizontal, vertical)
|
||||||
|
- **MiniMessage** formatting.
|
||||||
|
- Placeholders support through [PlaceholderAPI](https://github.com/PlaceholderAPI/PlaceholderAPI)
|
||||||
|
and [MiniPlaceholders](https://github.com/MiniPlaceholders/MiniPlaceholders) integration.
|
||||||
|
- [FancyNpcs](ttps://github.com/FancyMcPlugins/FancyNpcs) integration.
|
||||||
|
- ...and much more!
|
||||||
|
|
||||||
|
Check out **[images section](#images)** down below.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Paper **1.19.4** - **1.21.4** with **Java 21** (or higher) is required. Plugin should also work on **Paper** forks.
|
||||||
|
|
||||||
|
**Spigot** is **not** supported.
|
||||||
|
|
||||||
|
### Download (Stable)
|
||||||
|
|
||||||
|
- **[Hangar](https://hangar.papermc.io/Oliver/FancyHolograms)**
|
||||||
|
- **[Modrinth](https://modrinth.com/plugin/fancyholograms)**
|
||||||
|
- **[GitHub Releases](https://github.com/FancyMcPlugins/FancyHolograms/releases)**
|
||||||
|
|
||||||
|
### Download (Development Builds)
|
||||||
|
|
||||||
|
- **[Jenkins CI](https://jenkins.fancyplugins.de/job/FancyHolograms/)**
|
||||||
|
- **[FancyPlugins Website](https://fancyplugins.de/FancyHolograms/download)**
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Official documentation is hosted **[here](https://fancyplugins.de/docs/fancyholograms.html)**. Quick reference:
|
||||||
|
|
||||||
|
- **[Getting Started](https://fancyplugins.de/docs/fh-getting-started.html)**
|
||||||
|
- **[Command Reference](https://fancyplugins.de/docs/fh-commands.html)**
|
||||||
|
- **[Using API](https://fancyplugins.de/docs/fh-api.html)**
|
||||||
|
|
||||||
|
**Have more questions?** Feel free to ask them on our **[Discord](https://discord.gg/ZUgYCEJUEx)** server.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Developer API
|
||||||
|
|
||||||
|
More information can be found in **[Documentation](https://fancyplugins.de/docs/fh-api.html)**
|
||||||
|
and [Javadocs](https://fancyplugins.de/javadocs/fancyholograms/).
|
||||||
|
|
||||||
|
### Maven
|
||||||
|
|
||||||
|
```xml
|
||||||
|
|
||||||
|
<repository>
|
||||||
|
<id>fancyplugins-releases</id>
|
||||||
|
<name>FancyPlugins Repository</name>
|
||||||
|
<url>https://repo.fancyplugins.de/releases</url>
|
||||||
|
</repository>
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.oliver</groupId>
|
||||||
|
<artifactId>FancyHolograms</artifactId>
|
||||||
|
<version>[VERSION]</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gradle
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
repositories {
|
||||||
|
maven("https://repo.fancyplugins.de/releases")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("de.oliver:FancyHolograms:[VERSION]")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Follow these steps to build the plugin locally:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Cloning repository.
|
||||||
|
$ git clone https://github.com/FancyMcPlugins/FancyHolograms.git
|
||||||
|
# Entering cloned repository.
|
||||||
|
$ cd FancyHolograms
|
||||||
|
# Compiling and building artifacts.
|
||||||
|
$ gradlew shadowJar
|
||||||
|
# Once successfully built, plugin .jar can be found in /build/libs directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
Images showcasing the plugin, sent to us by our community.
|
||||||
|
|
||||||
|

|
||||||
|
<sup>Provided by [@OliverSchlueter](https://github.com/OliverSchlueter)</sup>
|
||||||
|
|
||||||
|

|
||||||
|
<sup>Provided by [@OliverSchlueter](https://github.com/OliverSchlueter)</sup>
|
||||||
|
|
||||||
|

|
||||||
|
<sup>Provided by [@OliverSchlueter](https://github.com/OliverSchlueter)</sup>
|
||||||
|
|
||||||
|

|
||||||
|
<sup>Provided by [@OliverSchlueter](https://github.com/OliverSchlueter)</sup>
|
||||||
|
|
||||||
|

|
||||||
|
<sup>Provided by [@OliverSchlueter](https://github.com/OliverSchlueter)</sup>
|
||||||
71
plugins/fancyholograms-v2/api/build.gradle.kts
Normal file
71
plugins/fancyholograms-v2/api/build.gradle.kts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("maven-publish")
|
||||||
|
id("com.gradleup.shadow")
|
||||||
|
}
|
||||||
|
|
||||||
|
val minecraftVersion = "1.19.4"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("io.papermc.paper:paper-api:$minecraftVersion-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
compileOnly("de.oliver:FancyLib:36")
|
||||||
|
compileOnly("de.oliver.FancyAnalytics:logger:0.0.6")
|
||||||
|
|
||||||
|
implementation("org.lushplugins:ChatColorHandler:5.1.2")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
shadowJar {
|
||||||
|
relocate("org.lushplugins.chatcolorhandler", "de.oliver.fancyholograms.libs.chatcolorhandler")
|
||||||
|
|
||||||
|
archiveClassifier.set("")
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "fancypluginsReleases"
|
||||||
|
url = uri("https://repo.fancyplugins.de/releases")
|
||||||
|
credentials(PasswordCredentials::class)
|
||||||
|
authentication {
|
||||||
|
isAllowInsecureProtocol = true
|
||||||
|
create<BasicAuthentication>("basic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
name = "fancypluginsSnapshots"
|
||||||
|
url = uri("https://repo.fancyplugins.de/snapshots")
|
||||||
|
credentials(PasswordCredentials::class)
|
||||||
|
authentication {
|
||||||
|
isAllowInsecureProtocol = true
|
||||||
|
create<BasicAuthentication>("basic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("maven") {
|
||||||
|
groupId = "de.oliver"
|
||||||
|
artifactId = "FancyHolograms"
|
||||||
|
version = project.version.toString()
|
||||||
|
from(project.components["java"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
|
||||||
|
options.release.set(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package de.oliver.fancyholograms.api;
|
||||||
|
|
||||||
|
import de.oliver.fancyanalytics.logger.ExtendedFancyLogger;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
public interface FancyHologramsPlugin {
|
||||||
|
|
||||||
|
static FancyHologramsPlugin get() {
|
||||||
|
if (isEnabled()) {
|
||||||
|
return EnabledChecker.getPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NullPointerException("Plugin is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isEnabled() {
|
||||||
|
return EnabledChecker.isFancyHologramsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
JavaPlugin getPlugin();
|
||||||
|
|
||||||
|
ExtendedFancyLogger getFancyLogger();
|
||||||
|
|
||||||
|
HologramManager getHologramManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration of the plugin.
|
||||||
|
*
|
||||||
|
* @return The configuration.
|
||||||
|
*/
|
||||||
|
HologramConfiguration getHologramConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the configuration of the plugin.
|
||||||
|
*
|
||||||
|
* @param configuration The new configuration.
|
||||||
|
* @param reload Whether the configuration should be reloaded.
|
||||||
|
*/
|
||||||
|
void setHologramConfiguration(HologramConfiguration configuration, boolean reload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The hologram storage.
|
||||||
|
*/
|
||||||
|
HologramStorage getHologramStorage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The hologram thread
|
||||||
|
*/
|
||||||
|
ScheduledExecutorService getHologramThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hologram storage.
|
||||||
|
*
|
||||||
|
* @param storage The new hologram storage.
|
||||||
|
* @param reload Whether the current hologram cache should be reloaded.
|
||||||
|
*/
|
||||||
|
void setHologramStorage(HologramStorage storage, boolean reload);
|
||||||
|
|
||||||
|
class EnabledChecker {
|
||||||
|
|
||||||
|
private static Boolean enabled;
|
||||||
|
private static FancyHologramsPlugin plugin;
|
||||||
|
|
||||||
|
public static Boolean isFancyHologramsEnabled() {
|
||||||
|
if (enabled != null) return enabled;
|
||||||
|
|
||||||
|
Plugin pl = Bukkit.getPluginManager().getPlugin("FancyHolograms");
|
||||||
|
|
||||||
|
if (pl != null && pl.isEnabled()) {
|
||||||
|
try {
|
||||||
|
plugin = (FancyHologramsPlugin) pl;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new IllegalStateException("API failed to access plugin, if using the FancyHolograms API make sure to set the dependency to compile only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FancyHologramsPlugin getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package de.oliver.fancyholograms.api;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public interface HologramConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the configuration.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin instance.
|
||||||
|
*/
|
||||||
|
void reload(@NotNull FancyHologramsPlugin plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether version notifications are muted.
|
||||||
|
*
|
||||||
|
* @return {@code true} if version notifications are muted, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean areVersionNotificationsMuted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether autosave is enabled.
|
||||||
|
*
|
||||||
|
* @return {@code true} if autosave is enabled, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean isAutosaveEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interval at which autosave is performed.
|
||||||
|
*
|
||||||
|
* @return The autosave interval in minutes.
|
||||||
|
*/
|
||||||
|
int getAutosaveInterval();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the plugin should save holograms when they are changed.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the plugin should save holograms when they are changed, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean isSaveOnChangedEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default visibility distance for holograms.
|
||||||
|
*
|
||||||
|
* @return The default hologram visibility distance.
|
||||||
|
*/
|
||||||
|
int getDefaultVisibilityDistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the plugin should register its commands.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the plugin should register its commands, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean isRegisterCommands();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the log level for the plugin.
|
||||||
|
*
|
||||||
|
* @return The log level for the plugin.
|
||||||
|
*/
|
||||||
|
String getLogLevel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interval at which hologram visibility is updated.
|
||||||
|
*
|
||||||
|
* @return The hologram visibility update interval in milliseconds.
|
||||||
|
*/
|
||||||
|
int getUpdateVisibilityInterval();
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package de.oliver.fancyholograms.api;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface HologramManager {
|
||||||
|
|
||||||
|
Optional<Hologram> getHologram(String name);
|
||||||
|
|
||||||
|
Collection<Hologram> getPersistentHolograms();
|
||||||
|
|
||||||
|
Collection<Hologram> getHolograms();
|
||||||
|
|
||||||
|
void addHologram(Hologram hologram);
|
||||||
|
|
||||||
|
void removeHologram(Hologram hologram);
|
||||||
|
|
||||||
|
Hologram create(HologramData hologramData);
|
||||||
|
|
||||||
|
void loadHolograms();
|
||||||
|
|
||||||
|
void saveHolograms();
|
||||||
|
|
||||||
|
void reloadHolograms();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package de.oliver.fancyholograms.api;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface HologramStorage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a collection of holograms.
|
||||||
|
*
|
||||||
|
* @param holograms The holograms to save.
|
||||||
|
* @param override Whether to override existing holograms.
|
||||||
|
*/
|
||||||
|
void saveBatch(Collection<Hologram> holograms, boolean override);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a hologram.
|
||||||
|
*
|
||||||
|
* @param hologram The hologram to save.
|
||||||
|
*/
|
||||||
|
void save(Hologram hologram);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a hologram.
|
||||||
|
*
|
||||||
|
* @param hologram The hologram to delete.
|
||||||
|
*/
|
||||||
|
void delete(Hologram hologram);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all holograms from all worlds
|
||||||
|
*
|
||||||
|
* @return A collection of all loaded holograms.
|
||||||
|
*/
|
||||||
|
Collection<Hologram> loadAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all holograms from a specific world
|
||||||
|
*
|
||||||
|
* @param world The world to load the holograms from.
|
||||||
|
* @return A collection of all loaded holograms.
|
||||||
|
*/
|
||||||
|
Collection<Hologram> loadAll(String world);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class BlockHologramData extends DisplayHologramData {
|
||||||
|
|
||||||
|
public static Material DEFAULT_BLOCK = Material.GRASS_BLOCK;
|
||||||
|
|
||||||
|
private Material block = DEFAULT_BLOCK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name Name of hologram
|
||||||
|
* @param location Location of hologram
|
||||||
|
* Default values are already set
|
||||||
|
*/
|
||||||
|
public BlockHologramData(String name, Location location) {
|
||||||
|
super(name, HologramType.BLOCK, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material getBlock() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockHologramData setBlock(Material block) {
|
||||||
|
if (!Objects.equals(this.block, block)) {
|
||||||
|
this.block = block;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean read(ConfigurationSection section, String name) {
|
||||||
|
super.read(section, name);
|
||||||
|
block = Material.getMaterial(section.getString("block", "GRASS_BLOCK").toUpperCase());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(ConfigurationSection section, String name) {
|
||||||
|
super.write(section, name);
|
||||||
|
section.set("block", block.name());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockHologramData copy(String name) {
|
||||||
|
BlockHologramData blockHologramData = new BlockHologramData(name, getLocation());
|
||||||
|
blockHologramData
|
||||||
|
.setBlock(this.getBlock())
|
||||||
|
.setScale(this.getScale())
|
||||||
|
.setShadowRadius(this.getShadowRadius())
|
||||||
|
.setShadowStrength(this.getShadowStrength())
|
||||||
|
.setBillboard(this.getBillboard())
|
||||||
|
.setTranslation(this.getTranslation())
|
||||||
|
.setBrightness(this.getBrightness())
|
||||||
|
.setVisibilityDistance(getVisibilityDistance())
|
||||||
|
.setVisibility(this.getVisibility())
|
||||||
|
.setPersistent(this.isPersistent())
|
||||||
|
.setLinkedNpcName(getLinkedNpcName());
|
||||||
|
|
||||||
|
return blockHologramData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class DisplayHologramData extends HologramData {
|
||||||
|
|
||||||
|
public static final Display.Billboard DEFAULT_BILLBOARD = Display.Billboard.CENTER;
|
||||||
|
public static final Vector3f DEFAULT_SCALE = new Vector3f(1, 1, 1);
|
||||||
|
public static final Vector3f DEFAULT_TRANSLATION = new Vector3f(0, 0, 0);
|
||||||
|
public static final float DEFAULT_SHADOW_RADIUS = 0.0f;
|
||||||
|
public static final float DEFAULT_SHADOW_STRENGTH = 1.0f;
|
||||||
|
public static final int DEFAULT_INTERPOLATION_DURATION = 0;
|
||||||
|
|
||||||
|
private Display.Billboard billboard = DEFAULT_BILLBOARD;
|
||||||
|
private Vector3f scale = new Vector3f(DEFAULT_SCALE);
|
||||||
|
private Vector3f translation = new Vector3f(DEFAULT_TRANSLATION);
|
||||||
|
private Display.Brightness brightness;
|
||||||
|
private float shadowRadius = DEFAULT_SHADOW_RADIUS;
|
||||||
|
private float shadowStrength = DEFAULT_SHADOW_STRENGTH;
|
||||||
|
private int interpolationDuration = DEFAULT_INTERPOLATION_DURATION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name Name of hologram
|
||||||
|
* @param type Type of hologram
|
||||||
|
* @param location Location of hologram
|
||||||
|
* Default values are already set
|
||||||
|
*/
|
||||||
|
public DisplayHologramData(String name, HologramType type, Location location) {
|
||||||
|
super(name, type, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Display.Billboard getBillboard() {
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setBillboard(Display.Billboard billboard) {
|
||||||
|
if (!Objects.equals(this.billboard, billboard)) {
|
||||||
|
this.billboard = billboard;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setScale(Vector3f scale) {
|
||||||
|
if (!Objects.equals(this.scale, scale)) {
|
||||||
|
this.scale = scale;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getTranslation() {
|
||||||
|
return translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setTranslation(Vector3f translation) {
|
||||||
|
if (!Objects.equals(this.translation, translation)) {
|
||||||
|
this.translation = translation;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Display.Brightness getBrightness() {
|
||||||
|
return brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setBrightness(Display.Brightness brightness) {
|
||||||
|
if (!Objects.equals(this.brightness, brightness)) {
|
||||||
|
this.brightness = brightness;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getShadowRadius() {
|
||||||
|
return shadowRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setShadowRadius(float shadowRadius) {
|
||||||
|
if (this.shadowRadius != shadowRadius) {
|
||||||
|
this.shadowRadius = shadowRadius;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getShadowStrength() {
|
||||||
|
return shadowStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayHologramData setShadowStrength(float shadowStrength) {
|
||||||
|
if (this.shadowStrength != shadowStrength) {
|
||||||
|
this.shadowStrength = shadowStrength;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Experimental
|
||||||
|
public int getInterpolationDuration() {
|
||||||
|
return interpolationDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Experimental
|
||||||
|
public DisplayHologramData setInterpolationDuration(int interpolationDuration) {
|
||||||
|
if (this.interpolationDuration != interpolationDuration) {
|
||||||
|
this.interpolationDuration = interpolationDuration;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean read(ConfigurationSection section, String name) {
|
||||||
|
super.read(section, name);
|
||||||
|
scale = new Vector3f(
|
||||||
|
(float) section.getDouble("scale_x", DEFAULT_SCALE.x),
|
||||||
|
(float) section.getDouble("scale_y", DEFAULT_SCALE.y),
|
||||||
|
(float) section.getDouble("scale_z", DEFAULT_SCALE.z)
|
||||||
|
);
|
||||||
|
|
||||||
|
translation = new Vector3f(
|
||||||
|
(float) section.getDouble("translation_x", DEFAULT_TRANSLATION.x),
|
||||||
|
(float) section.getDouble("translation_y", DEFAULT_TRANSLATION.y),
|
||||||
|
(float) section.getDouble("translation_z", DEFAULT_TRANSLATION.z)
|
||||||
|
);
|
||||||
|
|
||||||
|
shadowRadius = (float) section.getDouble("shadow_radius", DEFAULT_SHADOW_RADIUS);
|
||||||
|
shadowStrength = (float) section.getDouble("shadow_strength", DEFAULT_SHADOW_STRENGTH);
|
||||||
|
|
||||||
|
String billboardStr = section.getString("billboard", DEFAULT_BILLBOARD.name());
|
||||||
|
billboard = switch (billboardStr.toLowerCase()) {
|
||||||
|
case "fixed" -> Display.Billboard.FIXED;
|
||||||
|
case "vertical" -> Display.Billboard.VERTICAL;
|
||||||
|
case "horizontal" -> Display.Billboard.HORIZONTAL;
|
||||||
|
default -> Display.Billboard.CENTER;
|
||||||
|
};
|
||||||
|
|
||||||
|
int blockBrightness = Math.min(15, section.getInt("block_brightness", -1));
|
||||||
|
int skyBrightness = Math.min(15, section.getInt("sky_brightness", -1));
|
||||||
|
|
||||||
|
if(blockBrightness > -1 || skyBrightness > -1) {
|
||||||
|
brightness = new Display.Brightness(
|
||||||
|
Math.max(0, blockBrightness),
|
||||||
|
Math.max(0, skyBrightness)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(ConfigurationSection section, String name) {
|
||||||
|
super.write(section, name);
|
||||||
|
section.set("scale_x", scale.x);
|
||||||
|
section.set("scale_y", scale.y);
|
||||||
|
section.set("scale_z", scale.z);
|
||||||
|
section.set("translation_x", translation.x);
|
||||||
|
section.set("translation_y", translation.y);
|
||||||
|
section.set("translation_z", translation.z);
|
||||||
|
section.set("shadow_radius", shadowRadius);
|
||||||
|
section.set("shadow_strength", shadowStrength);
|
||||||
|
|
||||||
|
if(brightness != null) {
|
||||||
|
section.set("block_brightness", brightness.getBlockLight());
|
||||||
|
section.set("sky_brightness", brightness.getSkyLight());
|
||||||
|
}
|
||||||
|
|
||||||
|
section.set("billboard", billboard != Display.Billboard.CENTER ? billboard.name().toLowerCase(Locale.ROOT) : null);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DisplayHologramData copy(String name) {
|
||||||
|
DisplayHologramData displayHologramData = new DisplayHologramData(name, getType(), getLocation());
|
||||||
|
displayHologramData
|
||||||
|
.setScale(this.getScale())
|
||||||
|
.setShadowRadius(this.getShadowRadius())
|
||||||
|
.setShadowStrength(this.getShadowStrength())
|
||||||
|
.setBillboard(this.getBillboard())
|
||||||
|
.setTranslation(this.getTranslation())
|
||||||
|
.setBrightness(this.getBrightness())
|
||||||
|
.setVisibilityDistance(this.getVisibilityDistance())
|
||||||
|
.setVisibility(this.getVisibility())
|
||||||
|
.setPersistent(this.isPersistent())
|
||||||
|
.setLinkedNpcName(this.getLinkedNpcName());
|
||||||
|
|
||||||
|
return displayHologramData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.FancyHologramsPlugin;
|
||||||
|
import de.oliver.fancyholograms.api.data.property.Visibility;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class HologramData implements YamlData {
|
||||||
|
|
||||||
|
public static final int DEFAULT_VISIBILITY_DISTANCE = -1;
|
||||||
|
public static final Visibility DEFAULT_VISIBILITY = Visibility.ALL;
|
||||||
|
public static final boolean DEFAULT_IS_VISIBLE = true;
|
||||||
|
public static final boolean DEFAULT_PERSISTENCE = true;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final HologramType type;
|
||||||
|
private Location location;
|
||||||
|
private boolean hasChanges;
|
||||||
|
private int visibilityDistance = DEFAULT_VISIBILITY_DISTANCE;
|
||||||
|
private Visibility visibility = DEFAULT_VISIBILITY;
|
||||||
|
private boolean persistent = DEFAULT_PERSISTENCE;
|
||||||
|
private String linkedNpcName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name Name of hologram
|
||||||
|
* @param type Type of hologram
|
||||||
|
* @param location Location of hologram
|
||||||
|
* Default values are already set
|
||||||
|
*/
|
||||||
|
public HologramData(String name, HologramType type, Location location) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull HologramType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Location getLocation() {
|
||||||
|
return location.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramData setLocation(@Nullable Location location) {
|
||||||
|
this.location = location != null ? location.clone() : null;
|
||||||
|
setHasChanges(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether the hologram needs to send an update to players
|
||||||
|
*/
|
||||||
|
public final boolean hasChanges() {
|
||||||
|
return hasChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hasChanges Whether the hologram needs to send an update to players
|
||||||
|
*/
|
||||||
|
public final void setHasChanges(boolean hasChanges) {
|
||||||
|
this.hasChanges = hasChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVisibilityDistance() {
|
||||||
|
if (visibilityDistance > 0) {
|
||||||
|
return visibilityDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FancyHologramsPlugin.get().getHologramConfiguration().getDefaultVisibilityDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramData setVisibilityDistance(int visibilityDistance) {
|
||||||
|
this.visibilityDistance = visibilityDistance;
|
||||||
|
setHasChanges(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of visibility for the hologram.
|
||||||
|
*
|
||||||
|
* @return type of visibility.
|
||||||
|
*/
|
||||||
|
public Visibility getVisibility() {
|
||||||
|
return this.visibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the type of visibility for the hologram.
|
||||||
|
*/
|
||||||
|
public HologramData setVisibility(@NotNull Visibility visibility) {
|
||||||
|
if (!Objects.equals(this.visibility, visibility)) {
|
||||||
|
this.visibility = visibility;
|
||||||
|
setHasChanges(true);
|
||||||
|
|
||||||
|
if (this.visibility.equals(Visibility.MANUAL)) {
|
||||||
|
Visibility.ManualVisibility.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPersistent() {
|
||||||
|
return persistent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramData setPersistent(boolean persistent) {
|
||||||
|
this.persistent = persistent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLinkedNpcName() {
|
||||||
|
return linkedNpcName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramData setLinkedNpcName(String linkedNpcName) {
|
||||||
|
if (!Objects.equals(this.linkedNpcName, linkedNpcName)) {
|
||||||
|
this.linkedNpcName = linkedNpcName;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean read(ConfigurationSection section, String name) {
|
||||||
|
String worldName = section.getString("location.world", "world");
|
||||||
|
float x = (float) section.getDouble("location.x", 0);
|
||||||
|
float y = (float) section.getDouble("location.y", 0);
|
||||||
|
float z = (float) section.getDouble("location.z", 0);
|
||||||
|
float yaw = (float) section.getDouble("location.yaw", 0);
|
||||||
|
float pitch = (float) section.getDouble("location.pitch", 0);
|
||||||
|
|
||||||
|
World world = Bukkit.getWorld(worldName);
|
||||||
|
if (world == null) {
|
||||||
|
FancyHologramsPlugin.get().getFancyLogger().warn("Could not load hologram '" + name + "', because the world '" + worldName + "' is not loaded");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = new Location(world, x, y, z, yaw, pitch);
|
||||||
|
visibilityDistance = section.getInt("visibility_distance", DEFAULT_VISIBILITY_DISTANCE);
|
||||||
|
visibility = Optional.ofNullable(section.getString("visibility"))
|
||||||
|
.flatMap(Visibility::byString)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
final var visibleByDefault = section.getBoolean("visible_by_default", DisplayHologramData.DEFAULT_IS_VISIBLE);
|
||||||
|
return visibleByDefault ? Visibility.ALL : Visibility.PERMISSION_REQUIRED;
|
||||||
|
});
|
||||||
|
linkedNpcName = section.getString("linkedNpc");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(ConfigurationSection section, String name) {
|
||||||
|
section.set("type", type.name());
|
||||||
|
section.set("location.world", location.getWorld().getName());
|
||||||
|
section.set("location.x", location.x());
|
||||||
|
section.set("location.y", location.y());
|
||||||
|
section.set("location.z", location.z());
|
||||||
|
section.set("location.yaw", location.getYaw());
|
||||||
|
section.set("location.pitch", location.getPitch());
|
||||||
|
|
||||||
|
section.set("visibility_distance", visibilityDistance);
|
||||||
|
section.set("visibility", visibility.name());
|
||||||
|
section.set("persistent", persistent);
|
||||||
|
section.set("linkedNpc", linkedNpcName);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramData copy(String name) {
|
||||||
|
return new HologramData(name, type, this.getLocation())
|
||||||
|
.setVisibilityDistance(this.getVisibilityDistance())
|
||||||
|
.setVisibility(this.getVisibility())
|
||||||
|
.setPersistent(this.isPersistent())
|
||||||
|
.setLinkedNpcName(this.getLinkedNpcName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ItemHologramData extends DisplayHologramData {
|
||||||
|
|
||||||
|
public static final ItemStack DEFAULT_ITEM = new ItemStack(Material.APPLE);
|
||||||
|
|
||||||
|
private ItemStack item = DEFAULT_ITEM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name Name of hologram
|
||||||
|
* @param location Location of hologram
|
||||||
|
* Default values are already set
|
||||||
|
*/
|
||||||
|
public ItemHologramData(String name, Location location) {
|
||||||
|
super(name, HologramType.ITEM, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemHologramData setItemStack(ItemStack item) {
|
||||||
|
if (!Objects.equals(this.item, item)) {
|
||||||
|
this.item = item;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean read(ConfigurationSection section, String name) {
|
||||||
|
super.read(section, name);
|
||||||
|
item = section.getItemStack("item", DEFAULT_ITEM);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(ConfigurationSection section, String name) {
|
||||||
|
super.write(section, name);
|
||||||
|
section.set("item", item);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemHologramData copy(String name) {
|
||||||
|
ItemHologramData itemHologramData = new ItemHologramData(name, getLocation());
|
||||||
|
itemHologramData
|
||||||
|
.setItemStack(this.getItemStack())
|
||||||
|
.setScale(this.getScale())
|
||||||
|
.setShadowRadius(this.getShadowRadius())
|
||||||
|
.setShadowStrength(this.getShadowStrength())
|
||||||
|
.setBillboard(this.getBillboard())
|
||||||
|
.setTranslation(this.getTranslation())
|
||||||
|
.setBrightness(this.getBrightness())
|
||||||
|
.setVisibilityDistance(this.getVisibilityDistance())
|
||||||
|
.setVisibility(this.getVisibility())
|
||||||
|
.setPersistent(this.isPersistent())
|
||||||
|
.setLinkedNpcName(this.getLinkedNpcName());
|
||||||
|
|
||||||
|
return itemHologramData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Color;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.TextDisplay;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class TextHologramData extends DisplayHologramData {
|
||||||
|
|
||||||
|
public static final TextDisplay.TextAlignment DEFAULT_TEXT_ALIGNMENT = TextDisplay.TextAlignment.CENTER;
|
||||||
|
public static final boolean DEFAULT_TEXT_SHADOW_STATE = false;
|
||||||
|
public static final boolean DEFAULT_SEE_THROUGH = false;
|
||||||
|
public static final int DEFAULT_TEXT_UPDATE_INTERVAL = -1;
|
||||||
|
|
||||||
|
private List<String> text;
|
||||||
|
private Color background;
|
||||||
|
private TextDisplay.TextAlignment textAlignment = DEFAULT_TEXT_ALIGNMENT;
|
||||||
|
private boolean textShadow = DEFAULT_TEXT_SHADOW_STATE;
|
||||||
|
private boolean seeThrough = DEFAULT_SEE_THROUGH;
|
||||||
|
private int textUpdateInterval = DEFAULT_TEXT_UPDATE_INTERVAL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name Name of hologram
|
||||||
|
* @param location Location of hologram
|
||||||
|
* Default values are already set
|
||||||
|
*/
|
||||||
|
public TextHologramData(String name, Location location) {
|
||||||
|
super(name, HologramType.TEXT, location);
|
||||||
|
text = new ArrayList<>(List.of("Edit this line with /hologram edit " + name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setText(List<String> text) {
|
||||||
|
if (!Objects.equals(this.text, text)) {
|
||||||
|
this.text = text;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLine(String line) {
|
||||||
|
text.add(line);
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLine(int index) {
|
||||||
|
text.remove(index);
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getBackground() {
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setBackground(Color background) {
|
||||||
|
if (!Objects.equals(this.background, background)) {
|
||||||
|
this.background = background;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextDisplay.TextAlignment getTextAlignment() {
|
||||||
|
return textAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setTextAlignment(TextDisplay.TextAlignment textAlignment) {
|
||||||
|
if (!Objects.equals(this.textAlignment, textAlignment)) {
|
||||||
|
this.textAlignment = textAlignment;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTextShadow() {
|
||||||
|
return textShadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setTextShadow(boolean textShadow) {
|
||||||
|
if (this.textShadow != textShadow) {
|
||||||
|
this.textShadow = textShadow;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSeeThrough() {
|
||||||
|
return seeThrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setSeeThrough(boolean seeThrough) {
|
||||||
|
if (this.seeThrough != seeThrough) {
|
||||||
|
this.seeThrough = seeThrough;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTextUpdateInterval() {
|
||||||
|
return textUpdateInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextHologramData setTextUpdateInterval(int textUpdateInterval) {
|
||||||
|
if (this.textUpdateInterval != textUpdateInterval) {
|
||||||
|
this.textUpdateInterval = textUpdateInterval;
|
||||||
|
setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean read(ConfigurationSection section, String name) {
|
||||||
|
super.read(section, name);
|
||||||
|
text = section.getStringList("text");
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
text = List.of("Could not load hologram text");
|
||||||
|
//TODO: maybe return false here?
|
||||||
|
}
|
||||||
|
|
||||||
|
textShadow = section.getBoolean("text_shadow", DEFAULT_TEXT_SHADOW_STATE);
|
||||||
|
seeThrough = section.getBoolean("see_through", DEFAULT_SEE_THROUGH);
|
||||||
|
textUpdateInterval = section.getInt("update_text_interval", DEFAULT_TEXT_UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
String textAlignmentStr = section.getString("text_alignment", DEFAULT_TEXT_ALIGNMENT.name().toLowerCase());
|
||||||
|
textAlignment = switch (textAlignmentStr.toLowerCase(Locale.ROOT)) {
|
||||||
|
case "right" -> TextDisplay.TextAlignment.RIGHT;
|
||||||
|
case "left" -> TextDisplay.TextAlignment.LEFT;
|
||||||
|
default -> TextDisplay.TextAlignment.CENTER;
|
||||||
|
};
|
||||||
|
|
||||||
|
background = null;
|
||||||
|
String backgroundStr = section.getString("background", null);
|
||||||
|
if (backgroundStr != null) {
|
||||||
|
if (backgroundStr.equalsIgnoreCase("transparent")) {
|
||||||
|
background = Hologram.TRANSPARENT;
|
||||||
|
} else if (backgroundStr.startsWith("#")) {
|
||||||
|
background = Color.fromARGB((int) Long.parseLong(backgroundStr.substring(1), 16));
|
||||||
|
//backwards compatibility, make rgb hex colors solid color -their alpha is 0 by default-
|
||||||
|
if (backgroundStr.length() == 7) background = background.setAlpha(255);
|
||||||
|
} else {
|
||||||
|
background = Color.fromARGB(NamedTextColor.NAMES.value(backgroundStr.toLowerCase(Locale.ROOT).trim().replace(' ', '_')).value() | 0xC8000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(ConfigurationSection section, String name) {
|
||||||
|
super.write(section, name);
|
||||||
|
section.set("text", text);
|
||||||
|
section.set("text_shadow", textShadow);
|
||||||
|
section.set("see_through", seeThrough);
|
||||||
|
section.set("text_alignment", textAlignment.name().toLowerCase(Locale.ROOT));
|
||||||
|
section.set("update_text_interval", textUpdateInterval);
|
||||||
|
|
||||||
|
final String color;
|
||||||
|
if (background == null) {
|
||||||
|
color = null;
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
color = "transparent";
|
||||||
|
} else {
|
||||||
|
NamedTextColor named = background.getAlpha() == 255 ? NamedTextColor.namedColor(background.asRGB()) : null;
|
||||||
|
color = named != null ? named.toString() : '#' + Integer.toHexString(background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
section.set("background", color);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextHologramData copy(String name) {
|
||||||
|
TextHologramData textHologramData = new TextHologramData(name, getLocation());
|
||||||
|
textHologramData
|
||||||
|
.setText(this.getText())
|
||||||
|
.setBackground(this.getBackground())
|
||||||
|
.setTextAlignment(this.getTextAlignment())
|
||||||
|
.setTextShadow(this.hasTextShadow())
|
||||||
|
.setSeeThrough(this.isSeeThrough())
|
||||||
|
.setTextUpdateInterval(this.getTextUpdateInterval())
|
||||||
|
.setScale(this.getScale())
|
||||||
|
.setShadowRadius(this.getShadowRadius())
|
||||||
|
.setShadowStrength(this.getShadowStrength())
|
||||||
|
.setBillboard(this.getBillboard())
|
||||||
|
.setTranslation(this.getTranslation())
|
||||||
|
.setBrightness(this.getBrightness())
|
||||||
|
.setVisibilityDistance(this.getVisibilityDistance())
|
||||||
|
.setVisibility(this.getVisibility())
|
||||||
|
.setPersistent(this.isPersistent())
|
||||||
|
.setLinkedNpcName(this.getLinkedNpcName());
|
||||||
|
|
||||||
|
return textHologramData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
public interface YamlData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the data from the given configuration section.
|
||||||
|
*
|
||||||
|
* @return Whether the data was read successfully.
|
||||||
|
*/
|
||||||
|
boolean read(ConfigurationSection section, String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the data to the given configuration section.
|
||||||
|
*
|
||||||
|
* @return Whether the data was written successfully.
|
||||||
|
*/
|
||||||
|
boolean write(ConfigurationSection section, String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package de.oliver.fancyholograms.api.data.property;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public enum Visibility {
|
||||||
|
/**
|
||||||
|
* Everybody can see a hologram.
|
||||||
|
*/
|
||||||
|
ALL((player, hologram) -> true),
|
||||||
|
/**
|
||||||
|
* The player needs permission to see a specific hologram.
|
||||||
|
*/
|
||||||
|
PERMISSION_REQUIRED(
|
||||||
|
(player, hologram) -> player.hasPermission("fancyholograms.viewhologram." + hologram.getData().getName())
|
||||||
|
),
|
||||||
|
/**
|
||||||
|
* The player needs to be added manually through the API
|
||||||
|
*/
|
||||||
|
MANUAL(ManualVisibility::canSee);
|
||||||
|
|
||||||
|
private final VisibilityPredicate predicate;
|
||||||
|
|
||||||
|
Visibility(VisibilityPredicate predicate) {
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Visibility> byString(String value) {
|
||||||
|
return Arrays.stream(Visibility.values())
|
||||||
|
.filter(visibility -> visibility.toString().equalsIgnoreCase(value))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canSee(Player player, Hologram hologram) {
|
||||||
|
return this.predicate.canSee(player, hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface VisibilityPredicate {
|
||||||
|
boolean canSee(Player player, Hologram hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handling of Visibility.MANUAL
|
||||||
|
* <br>
|
||||||
|
* TODO: Discussion needed - Potentially condense this into one singular multimap within the enum?
|
||||||
|
*/
|
||||||
|
public static class ManualVisibility {
|
||||||
|
private static final HashMultimap<String, UUID> distantViewers = HashMultimap.create();
|
||||||
|
|
||||||
|
public static boolean canSee(Player player, Hologram hologram) {
|
||||||
|
return hologram.isViewer(player) || distantViewers.containsEntry(hologram.getName(), player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addDistantViewer(Hologram hologram, UUID uuid) {
|
||||||
|
addDistantViewer(hologram.getName(), uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addDistantViewer(String hologramName, UUID uuid) {
|
||||||
|
distantViewers.put(hologramName, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeDistantViewer(Hologram hologram, UUID uuid) {
|
||||||
|
removeDistantViewer(hologram.getName(), uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeDistantViewer(String hologramName, UUID uuid) {
|
||||||
|
distantViewers.remove(hologramName, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(Hologram hologram) {
|
||||||
|
remove(hologram.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(String hologramName) {
|
||||||
|
distantViewers.removeAll(hologramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
distantViewers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a hologram is being created, any hologram data changed will be reflected in the new hologram
|
||||||
|
*/
|
||||||
|
public final class HologramCreateEvent extends HologramEvent {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public HologramCreateEvent(@NotNull final Hologram hologram, @NotNull final Player player) {
|
||||||
|
super(hologram, false);
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Player getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a hologram is being deleted, any hologram data changed will be reflected in the hologram if
|
||||||
|
* the event is called
|
||||||
|
*/
|
||||||
|
public final class HologramDeleteEvent extends HologramEvent {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final CommandSender player;
|
||||||
|
|
||||||
|
public HologramDeleteEvent(@NotNull final Hologram hologram, @NotNull final CommandSender player) {
|
||||||
|
super(hologram, false);
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull CommandSender getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a base event related to Holograms. This is an abstract class that other event classes related to Holograms should extend.
|
||||||
|
* This event is cancellable, which means it can be prevented from being processed by the server.
|
||||||
|
*/
|
||||||
|
public abstract class HologramEvent extends Event implements Cancellable {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Hologram hologram;
|
||||||
|
|
||||||
|
|
||||||
|
private boolean cancelled;
|
||||||
|
|
||||||
|
|
||||||
|
protected HologramEvent(@NotNull final Hologram hologram, final boolean isAsync) {
|
||||||
|
super(isAsync);
|
||||||
|
this.hologram = hologram;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hologram involved in this event.
|
||||||
|
*
|
||||||
|
* @return the hologram involved in this event
|
||||||
|
*/
|
||||||
|
public final @NotNull Hologram getHologram() {
|
||||||
|
return this.hologram;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isCancelled() {
|
||||||
|
return this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void setCancelled(final boolean cancel) {
|
||||||
|
this.cancelled = cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a hologram is being hidden from a player
|
||||||
|
*/
|
||||||
|
public final class HologramHideEvent extends HologramEvent {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public HologramHideEvent(@NotNull final Hologram hologram, @NotNull final Player player) {
|
||||||
|
super(hologram, !Bukkit.isPrimaryThread());
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Player getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a hologram is being shown to a player
|
||||||
|
*/
|
||||||
|
public final class HologramShowEvent extends HologramEvent {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public HologramShowEvent(@NotNull final Hologram hologram, @NotNull final Player player) {
|
||||||
|
super(hologram, !Bukkit.isPrimaryThread());
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Player getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a hologram is being updated, the data in the hologram is current and the event holds the new data
|
||||||
|
*/
|
||||||
|
public final class HologramUpdateEvent extends HologramEvent {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
private final @NotNull CommandSender player;
|
||||||
|
private final @NotNull HologramData updatedData;
|
||||||
|
private final @NotNull HologramModification modification;
|
||||||
|
|
||||||
|
public HologramUpdateEvent(@NotNull final Hologram hologram, @NotNull final CommandSender player, @NotNull final HologramData updatedData, @NotNull final HologramModification modification) {
|
||||||
|
super(hologram, false);
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
this.updatedData = updatedData;
|
||||||
|
this.modification = modification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull CommandSender getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current data of the hologram.
|
||||||
|
*
|
||||||
|
* @return the current data of the hologram
|
||||||
|
*/
|
||||||
|
public @NotNull HologramData getCurrentData() {
|
||||||
|
return getHologram().getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the updated data of the hologram.
|
||||||
|
*
|
||||||
|
* @return the updated data of the hologram
|
||||||
|
*/
|
||||||
|
public @NotNull HologramData getUpdatedData() {
|
||||||
|
return this.updatedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of modification performed on the hologram.
|
||||||
|
*
|
||||||
|
* @return the type of modification performed on the hologram
|
||||||
|
*/
|
||||||
|
public @NotNull HologramModification getModification() {
|
||||||
|
return this.modification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the various types of modifications that can be made to a Hologram.
|
||||||
|
*/
|
||||||
|
public enum HologramModification {
|
||||||
|
TEXT,
|
||||||
|
POSITION,
|
||||||
|
SCALE,
|
||||||
|
TRANSLATION,
|
||||||
|
BILLBOARD,
|
||||||
|
BACKGROUND,
|
||||||
|
TEXT_SHADOW,
|
||||||
|
TEXT_ALIGNMENT,
|
||||||
|
SEE_THROUGH,
|
||||||
|
SHADOW_RADIUS,
|
||||||
|
SHADOW_STRENGTH,
|
||||||
|
UPDATE_TEXT_INTERVAL,
|
||||||
|
UPDATE_VISIBILITY_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class HologramsLoadedEvent extends Event {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
private final ImmutableList<Hologram> holograms;
|
||||||
|
|
||||||
|
public HologramsLoadedEvent(@NotNull final ImmutableList<Hologram> holograms) {
|
||||||
|
super(!Bukkit.isPrimaryThread());
|
||||||
|
|
||||||
|
this.holograms = holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ImmutableList<Hologram> getManager() {
|
||||||
|
return this.holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package de.oliver.fancyholograms.api.events;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class HologramsUnloadedEvent extends Event {
|
||||||
|
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
private final ImmutableList<Hologram> holograms;
|
||||||
|
|
||||||
|
public HologramsUnloadedEvent(@NotNull final ImmutableList<Hologram> holograms) {
|
||||||
|
super(!Bukkit.isPrimaryThread());
|
||||||
|
|
||||||
|
this.holograms = holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ImmutableList<Hologram> getManager() {
|
||||||
|
return this.holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
package de.oliver.fancyholograms.api.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.property.Visibility;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Color;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.lushplugins.chatcolorhandler.ModernChatColorHandler;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for creating, updating, and managing holograms.
|
||||||
|
* <p>
|
||||||
|
* This class provides the basic functionality needed to work with holograms
|
||||||
|
* across multiple versions of Minecraft. To create a hologram specific to a version of Minecraft,
|
||||||
|
* extend this class and implement the abstract methods.
|
||||||
|
* <p>
|
||||||
|
* Note that the specific way holograms are created, updated, and deleted
|
||||||
|
* will vary depending on the Minecraft version.
|
||||||
|
* <p>
|
||||||
|
* A Hologram object includes data about the hologram and maintains a set of players to whom the hologram is shown.
|
||||||
|
*/
|
||||||
|
public abstract class Hologram {
|
||||||
|
|
||||||
|
public static final int LINE_WIDTH = 1000;
|
||||||
|
public static final Color TRANSPARENT = Color.fromARGB(0);
|
||||||
|
protected static final int MINIMUM_PROTOCOL_VERSION = 762;
|
||||||
|
|
||||||
|
protected final @NotNull HologramData data;
|
||||||
|
/**
|
||||||
|
* Set of UUIDs of players to whom the hologram is currently shown.
|
||||||
|
*/
|
||||||
|
protected final @NotNull Set<UUID> viewers = new HashSet<>();
|
||||||
|
|
||||||
|
protected Hologram(@NotNull final HologramData data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getName() {
|
||||||
|
return data.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final @NotNull HologramData getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity id of this hologram
|
||||||
|
* This id is for packet use only as the entity is not registered to the server
|
||||||
|
* @return entity id
|
||||||
|
*/
|
||||||
|
public abstract int getEntityId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Display entity of this Hologram object.
|
||||||
|
* The entity is not registered in the world or server.
|
||||||
|
* Only use this method if you know what you're doing.
|
||||||
|
* <p>
|
||||||
|
* This method will return <code>null</code> in 1.20.5 and newer versions
|
||||||
|
*
|
||||||
|
* @return the Display entity of this Hologram object
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
@Deprecated(forRemoval = true, since = "2.4.1")
|
||||||
|
public abstract @Nullable Display getDisplayEntity();
|
||||||
|
|
||||||
|
protected abstract void create();
|
||||||
|
|
||||||
|
protected abstract void delete();
|
||||||
|
|
||||||
|
protected abstract void update();
|
||||||
|
|
||||||
|
protected abstract boolean show(@NotNull final Player player);
|
||||||
|
|
||||||
|
protected abstract boolean hide(@NotNull final Player player);
|
||||||
|
|
||||||
|
protected abstract void refresh(@NotNull final Player player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the hologram entity.
|
||||||
|
* Only run this if creating custom Hologram implementations as this is run in
|
||||||
|
* {@link de.oliver.fancyholograms.api.HologramManager#create(HologramData)}.
|
||||||
|
*/
|
||||||
|
public final void createHologram() {
|
||||||
|
create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the hologram entity.
|
||||||
|
*/
|
||||||
|
public final void deleteHologram() {
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the hologram to a collection of players.
|
||||||
|
* Use {@link #forceShowHologram(Player)} if this hologram is not registered to the HologramManager.
|
||||||
|
*
|
||||||
|
* @param players The players to show the hologram to
|
||||||
|
*/
|
||||||
|
public final void showHologram(Collection<? extends Player> players) {
|
||||||
|
players.forEach(this::showHologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the hologram to a player.
|
||||||
|
* Use {@link #forceShowHologram(Player)} if this hologram is not registered to the HologramManager.
|
||||||
|
*
|
||||||
|
* @param player The player to show the hologram to
|
||||||
|
*/
|
||||||
|
public final void showHologram(Player player) {
|
||||||
|
viewers.add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully shows the hologram to a player.
|
||||||
|
*
|
||||||
|
* @param player The player to show the hologram to
|
||||||
|
*/
|
||||||
|
public final void forceShowHologram(Player player) {
|
||||||
|
show(player);
|
||||||
|
|
||||||
|
if (this.getData().getVisibility().equals(Visibility.MANUAL)) {
|
||||||
|
Visibility.ManualVisibility.addDistantViewer(this, player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the hologram from a collection of players.
|
||||||
|
* Use {@link #forceHideHologram(Player)} if this hologram is not registered to the HologramManager.
|
||||||
|
*
|
||||||
|
* @param players The players to hide the hologram from
|
||||||
|
*/
|
||||||
|
public final void hideHologram(Collection<? extends Player> players) {
|
||||||
|
players.forEach(this::hideHologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the hologram from a player.
|
||||||
|
* Use {@link #forceHideHologram(Player)} if this hologram is not registered to the HologramManager.
|
||||||
|
*
|
||||||
|
* @param player The player to hide the hologram from
|
||||||
|
*/
|
||||||
|
public final void hideHologram(Player player) {
|
||||||
|
viewers.remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully hides the hologram from a player.
|
||||||
|
*
|
||||||
|
* @param player The player to show the hologram to
|
||||||
|
*/
|
||||||
|
public final void forceHideHologram(Player player) {
|
||||||
|
hide(player);
|
||||||
|
|
||||||
|
if (this.getData().getVisibility().equals(Visibility.MANUAL)) {
|
||||||
|
Visibility.ManualVisibility.removeDistantViewer(this, player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues hologram to update and refresh for players.
|
||||||
|
*
|
||||||
|
* @deprecated in favour of {@link #queueUpdate()}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public final void updateHologram() {
|
||||||
|
queueUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues hologram to update and refresh for players
|
||||||
|
* Use {@link #forceUpdate()} if this hologram is not registered to the HologramManager.
|
||||||
|
*/
|
||||||
|
public final void queueUpdate() {
|
||||||
|
data.setHasChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully updates and refreshes hologram for players.
|
||||||
|
*/
|
||||||
|
public final void forceUpdate() {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the hologram for the players currently viewing it.
|
||||||
|
*/
|
||||||
|
public void refreshForViewers() {
|
||||||
|
final var players = getViewers()
|
||||||
|
.stream()
|
||||||
|
.map(Bukkit::getPlayer)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
refreshHologram(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the hologram for players currently viewing it in the same world as the hologram.
|
||||||
|
*/
|
||||||
|
public void refreshForViewersInWorld() {
|
||||||
|
World world = data.getLocation().getWorld();
|
||||||
|
final var players = getViewers()
|
||||||
|
.stream()
|
||||||
|
.map(Bukkit::getPlayer)
|
||||||
|
.filter(player -> player != null && player.getWorld().equals(world))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
refreshHologram(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the hologram's data for a player.
|
||||||
|
*
|
||||||
|
* @param player the player to refresh for
|
||||||
|
*/
|
||||||
|
public final void refreshHologram(@NotNull final Player player) {
|
||||||
|
refresh(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the hologram's data for a collection of players.
|
||||||
|
*
|
||||||
|
* @param players the collection of players to refresh for
|
||||||
|
*/
|
||||||
|
public final void refreshHologram(@NotNull final Collection<? extends Player> players) {
|
||||||
|
players.forEach(this::refreshHologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a copy of the set of UUIDs of players currently viewing the hologram
|
||||||
|
*/
|
||||||
|
public final @NotNull Set<UUID> getViewers() {
|
||||||
|
return new HashSet<>(this.viewers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param player the player to check for
|
||||||
|
* @return whether the player is currently viewing the hologram
|
||||||
|
*/
|
||||||
|
public final boolean isViewer(@NotNull final Player player) {
|
||||||
|
return isViewer(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param player the uuid of the player to check for
|
||||||
|
* @return whether the player is currently viewing the hologram
|
||||||
|
*/
|
||||||
|
public final boolean isViewer(@NotNull final UUID player) {
|
||||||
|
return this.viewers.contains(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldShowTo(@NotNull final Player player) {
|
||||||
|
if (!meetsVisibilityConditions(player)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isWithinVisibilityDistance(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean meetsVisibilityConditions(@NotNull final Player player) {
|
||||||
|
return this.getData().getVisibility().canSee(player, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWithinVisibilityDistance(@NotNull final Player player) {
|
||||||
|
final var location = getData().getLocation();
|
||||||
|
if (!location.getWorld().equals(player.getWorld())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visibilityDistance = data.getVisibilityDistance();
|
||||||
|
double distanceSquared = location.distanceSquared(player.getLocation());
|
||||||
|
|
||||||
|
return distanceSquared <= visibilityDistance * visibilityDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks and updates the shown state for a player.
|
||||||
|
* If the hologram is shown and should not be, it hides it.
|
||||||
|
* If the hologram is not shown and should be, it shows it.
|
||||||
|
* Use {@link #forceUpdateShownStateFor(Player)} if this hologram is not registered to the HologramManager.
|
||||||
|
*
|
||||||
|
* @param player the player to check and update the shown state for
|
||||||
|
*/
|
||||||
|
public void updateShownStateFor(Player player) {
|
||||||
|
boolean isShown = isViewer(player);
|
||||||
|
boolean shouldBeShown = shouldShowTo(player);
|
||||||
|
|
||||||
|
if (isShown && !shouldBeShown) {
|
||||||
|
showHologram(player);
|
||||||
|
} else if (!isShown && shouldBeShown) {
|
||||||
|
hideHologram(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks and forcefully updates the shown state for a player.
|
||||||
|
* If the hologram is shown and should not be, it hides it.
|
||||||
|
* If the hologram is not shown and should be, it shows it.
|
||||||
|
*
|
||||||
|
* @param player the player to check and update the shown state for
|
||||||
|
*/
|
||||||
|
public void forceUpdateShownStateFor(Player player) {
|
||||||
|
boolean isShown = isViewer(player);
|
||||||
|
|
||||||
|
if (meetsVisibilityConditions(player)) {
|
||||||
|
if (isWithinVisibilityDistance(player)) {
|
||||||
|
// Ran if the player meets the visibility conditions and is within visibility distance
|
||||||
|
if (!isShown) {
|
||||||
|
show(player);
|
||||||
|
|
||||||
|
if (getData().getVisibility().equals(Visibility.MANUAL)) {
|
||||||
|
Visibility.ManualVisibility.removeDistantViewer(this, player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ran if the player meets the visibility conditions but is not within visibility distance
|
||||||
|
if (isShown) {
|
||||||
|
hide(player);
|
||||||
|
|
||||||
|
if (getData().getVisibility().equals(Visibility.MANUAL)) {
|
||||||
|
Visibility.ManualVisibility.addDistantViewer(this, player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ran if the player does not meet visibility conditions
|
||||||
|
if (isShown) {
|
||||||
|
hide(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the text shown in the hologram. If a player is specified, placeholders in the text are replaced
|
||||||
|
* with their corresponding values for the player.
|
||||||
|
*
|
||||||
|
* @param player the player to get the placeholders for, or null if no placeholders should be replaced
|
||||||
|
* @return the text shown in the hologram
|
||||||
|
*/
|
||||||
|
public final Component getShownText(@Nullable final Player player) {
|
||||||
|
if (!(getData() instanceof TextHologramData textData)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = String.join("\n", textData.getText());
|
||||||
|
|
||||||
|
return ModernChatColorHandler.translate(text, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(@Nullable final Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof Hologram that)) return false;
|
||||||
|
|
||||||
|
return Objects.equals(this.getData(), that.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return Objects.hash(this.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package de.oliver.fancyholograms.api.hologram;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum HologramType {
|
||||||
|
TEXT(Arrays.asList("background", "textshadow", "textalignment", "seethrough", "setline", "removeline", "addline", "insertbefore", "insertafter", "updatetextinterval")),
|
||||||
|
ITEM(List.of("item")),
|
||||||
|
BLOCK(List.of("block"));
|
||||||
|
|
||||||
|
private final List<String> commands;
|
||||||
|
|
||||||
|
HologramType(List<String> commands) {
|
||||||
|
this.commands = commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HologramType getByName(String name) {
|
||||||
|
for (HologramType type : values()) {
|
||||||
|
if (type.name().equalsIgnoreCase(name)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCommands() {
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
209
plugins/fancyholograms-v2/build.gradle.kts
Normal file
209
plugins/fancyholograms-v2/build.gradle.kts
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
|
||||||
|
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("maven-publish")
|
||||||
|
|
||||||
|
id("xyz.jpenilla.run-paper")
|
||||||
|
id("com.gradleup.shadow")
|
||||||
|
id("net.minecrell.plugin-yml.paper")
|
||||||
|
id("io.papermc.hangar-publish-plugin")
|
||||||
|
id("com.modrinth.minotaur")
|
||||||
|
}
|
||||||
|
|
||||||
|
runPaper.folia.registerTask()
|
||||||
|
|
||||||
|
val supportedVersions =
|
||||||
|
listOf(
|
||||||
|
"1.19.4",
|
||||||
|
"1.20",
|
||||||
|
"1.20.1",
|
||||||
|
"1.20.2",
|
||||||
|
"1.20.3",
|
||||||
|
"1.20.4",
|
||||||
|
"1.20.5",
|
||||||
|
"1.20.6",
|
||||||
|
"1.21",
|
||||||
|
"1.21.1",
|
||||||
|
"1.21.2",
|
||||||
|
"1.21.3",
|
||||||
|
"1.21.4",
|
||||||
|
)
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
group = "de.oliver"
|
||||||
|
val buildId = System.getenv("BUILD_ID")
|
||||||
|
version = "2.4.2" + (if (buildId != null) ".$buildId" else "")
|
||||||
|
description = "Simple, lightweight and fast hologram plugin using display entities"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
|
||||||
|
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.fancyplugins.de/releases")
|
||||||
|
maven(url = "https://repo.lushplugins.org/releases")
|
||||||
|
maven(url = "https://repo.viaversion.com/")
|
||||||
|
maven(url = "https://repo.opencollab.dev/main/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:api"))
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:implementation_1_20_4", configuration = "reobf"))
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:implementation_1_20_2", configuration = "reobf"))
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:implementation_1_20_1", configuration = "reobf"))
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:implementation_1_19_4", configuration = "reobf"))
|
||||||
|
|
||||||
|
implementation("de.oliver:FancyLib:36")
|
||||||
|
implementation("de.oliver:FancySitula:0.0.13")
|
||||||
|
implementation("de.oliver.FancyAnalytics:api:0.1.6")
|
||||||
|
implementation("de.oliver.FancyAnalytics:logger:0.0.6")
|
||||||
|
|
||||||
|
compileOnly("de.oliver:FancyNpcs:2.4.2")
|
||||||
|
compileOnly("org.lushplugins:ChatColorHandler:5.1.2")
|
||||||
|
compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
paper {
|
||||||
|
main = "de.oliver.fancyholograms.FancyHolograms"
|
||||||
|
bootstrapper = "de.oliver.fancyholograms.loaders.FancyHologramsBootstrapper"
|
||||||
|
loader = "de.oliver.fancyholograms.loaders.FancyHologramsLoader"
|
||||||
|
foliaSupported = true
|
||||||
|
version = rootProject.version.toString()
|
||||||
|
description = "Simple, lightweight and fast hologram plugin using display entities"
|
||||||
|
apiVersion = "1.19"
|
||||||
|
load = BukkitPluginDescription.PluginLoadOrder.POSTWORLD
|
||||||
|
serverDependencies {
|
||||||
|
register("FancyNpcs") {
|
||||||
|
required = false
|
||||||
|
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||||
|
}
|
||||||
|
register("MiniPlaceholders") {
|
||||||
|
required = false
|
||||||
|
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||||
|
}
|
||||||
|
register("PlaceholderAPI") {
|
||||||
|
required = false
|
||||||
|
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||||
|
}
|
||||||
|
register("floodgate") {
|
||||||
|
required = false
|
||||||
|
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||||
|
joinClasspath = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
runServer {
|
||||||
|
minecraftVersion("1.21.4")
|
||||||
|
|
||||||
|
downloadPlugins {
|
||||||
|
modrinth("fancynpcs", "2.4.0")
|
||||||
|
hangar("ViaVersion", "5.2.0")
|
||||||
|
hangar("ViaBackwards", "5.2.0")
|
||||||
|
// modrinth("multiverse-core", "4.3.11")
|
||||||
|
hangar("PlaceholderAPI", "2.11.6")
|
||||||
|
// modrinth("DecentHolograms", "2.8.12")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveClassifier.set("")
|
||||||
|
archiveBaseName.set("FancyHolograms")
|
||||||
|
|
||||||
|
dependsOn(":plugins:fancyholograms-v2:api:shadowJar")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||||
|
options.release = 21
|
||||||
|
// For cloud-annotations, see https://cloud.incendo.org/annotations/#command-components
|
||||||
|
options.compilerArgs.add("-parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
filteringCharset = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||||
|
|
||||||
|
val props = mapOf(
|
||||||
|
"description" to project.description,
|
||||||
|
"version" to project.version,
|
||||||
|
"hash" to gitCommitHash.get(),
|
||||||
|
"build" to (System.getenv("BUILD_ID") ?: "").ifEmpty { "undefined" }
|
||||||
|
)
|
||||||
|
|
||||||
|
inputs.properties(props)
|
||||||
|
|
||||||
|
filesMatching("paper-plugin.yml") {
|
||||||
|
expand(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
filesMatching("version.yml") {
|
||||||
|
expand(props)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.publishAllPublicationsToHangar {
|
||||||
|
dependsOn("shadowJar")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.modrinth {
|
||||||
|
dependsOn("shadowJar")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||||
|
}
|
||||||
|
|
||||||
|
val gitCommitHash: Provider<String> = providers.exec {
|
||||||
|
commandLine("git", "rev-parse", "HEAD")
|
||||||
|
}.standardOutput.asText.map { it.trim() }
|
||||||
|
|
||||||
|
val gitCommitMessage: Provider<String> = providers.exec {
|
||||||
|
commandLine("git", "log", "-1", "--pretty=%B")
|
||||||
|
}.standardOutput.asText.map { it.trim() }
|
||||||
|
|
||||||
|
hangarPublish {
|
||||||
|
publications.register("plugin") {
|
||||||
|
version = project.version as String
|
||||||
|
id = "FancyHolograms"
|
||||||
|
channel = "Alpha"
|
||||||
|
|
||||||
|
apiKey.set(System.getenv("HANGAR_PUBLISH_API_TOKEN"))
|
||||||
|
|
||||||
|
platforms {
|
||||||
|
paper {
|
||||||
|
jar = tasks.shadowJar.flatMap { it.archiveFile }
|
||||||
|
platformVersions = supportedVersions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changelog = gitCommitMessage.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
token.set(System.getenv("MODRINTH_PUBLISH_API_TOKEN"))
|
||||||
|
projectId.set("fancyholograms")
|
||||||
|
versionNumber.set(project.version.toString())
|
||||||
|
versionType.set("alpha")
|
||||||
|
uploadFile.set(file("build/libs/${project.name}-${project.version}.jar"))
|
||||||
|
gameVersions.addAll(supportedVersions)
|
||||||
|
loaders.add("paper")
|
||||||
|
loaders.add("folia")
|
||||||
|
changelog.set(gitCommitMessage.get())
|
||||||
|
}
|
||||||
BIN
plugins/fancyholograms-v2/images/banner.png
Normal file
BIN
plugins/fancyholograms-v2/images/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 KiB |
BIN
plugins/fancyholograms-v2/images/screenshots/example1.jpeg
Normal file
BIN
plugins/fancyholograms-v2/images/screenshots/example1.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 592 KiB |
BIN
plugins/fancyholograms-v2/images/screenshots/example2.jpeg
Normal file
BIN
plugins/fancyholograms-v2/images/screenshots/example2.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 550 KiB |
BIN
plugins/fancyholograms-v2/images/screenshots/example3.jpeg
Normal file
BIN
plugins/fancyholograms-v2/images/screenshots/example3.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 456 KiB |
BIN
plugins/fancyholograms-v2/images/screenshots/example4.jpeg
Normal file
BIN
plugins/fancyholograms-v2/images/screenshots/example4.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 474 KiB |
BIN
plugins/fancyholograms-v2/images/screenshots/example5.jpeg
Normal file
BIN
plugins/fancyholograms-v2/images/screenshots/example5.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 566 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("io.papermc.paperweight.userdev")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val minecraftVersion = "1.19.4"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:api"))
|
||||||
|
implementation("de.oliver:FancyLib:36")
|
||||||
|
compileOnly("com.viaversion:viaversion-api:5.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named("assemble") {
|
||||||
|
dependsOn(named("reobfJar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
|
||||||
|
options.release.set(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramHideEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancylib.ReflectionUtils;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
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.DataItem;
|
||||||
|
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.v1_19_R3.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_19_R3.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_19_4 extends Hologram {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Display display;
|
||||||
|
|
||||||
|
public Hologram1_19_4(@NotNull final HologramData data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEntityId() {
|
||||||
|
return display.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable org.bukkit.entity.Display getDisplayEntity() {
|
||||||
|
return display != null ? (org.bukkit.entity.Display) display.getBukkitEntity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create() {
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData dd) {
|
||||||
|
final var DATA_INTERPOLATION_DURATION_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_19_4.DATA_INTERPOLATION_DURATION_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_DURATION_ID, dd.getInterpolationDuration());
|
||||||
|
|
||||||
|
final var DATA_INTERPOLATION_START_DELTA_TICKS_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_19_4.DATA_INTERPOLATION_START_DELTA_TICKS_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_START_DELTA_TICKS_ID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.getLocation();
|
||||||
|
if (!location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
display.setPosRaw(location.x(), location.y(), location.z());
|
||||||
|
display.setYRot(location.getYaw());
|
||||||
|
display.setXRot(location.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display instanceof TextDisplay textDisplay && data instanceof TextHologramData textData) {
|
||||||
|
// line width
|
||||||
|
final var DATA_LINE_WIDTH_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_19_4.DATA_LINE_WIDTH_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_LINE_WIDTH_ID, Hologram.LINE_WIDTH);
|
||||||
|
|
||||||
|
// background
|
||||||
|
final var DATA_BACKGROUND_COLOR_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_19_4.DATA_BACKGROUND_COLOR_ID.getMapping());
|
||||||
|
|
||||||
|
final var background = textData.getBackground();
|
||||||
|
if (background == null) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, TextDisplay.INITIAL_BACKGROUND);
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, 0);
|
||||||
|
} else {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
// text shadow
|
||||||
|
if (textData.hasTextShadow()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SHADOW));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SHADOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// see through
|
||||||
|
if (textData.isSeeThrough()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 instanceof ItemHologramData itemData) {
|
||||||
|
// item
|
||||||
|
itemDisplay.setItemStack(ItemStack.fromBukkitCopy(itemData.getItemStack()));
|
||||||
|
|
||||||
|
} else if (display instanceof Display.BlockDisplay blockDisplay && data instanceof BlockHologramData blockData) {
|
||||||
|
Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + blockData.getBlock().name().toLowerCase(), ':'));
|
||||||
|
blockDisplay.setBlockState(block.defaultBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
// billboard data
|
||||||
|
display.setBillboardConstraints(switch (displayData.getBillboard()) {
|
||||||
|
case FIXED -> Display.BillboardConstraints.FIXED;
|
||||||
|
case VERTICAL -> Display.BillboardConstraints.VERTICAL;
|
||||||
|
case HORIZONTAL -> Display.BillboardConstraints.HORIZONTAL;
|
||||||
|
case CENTER -> Display.BillboardConstraints.CENTER;
|
||||||
|
});
|
||||||
|
|
||||||
|
// brightness
|
||||||
|
if (displayData.getBrightness() != null) {
|
||||||
|
display.setBrightnessOverride(new Brightness(displayData.getBrightness().getBlockLight(), displayData.getBrightness().getSkyLight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity scale AND MORE!
|
||||||
|
display.setTransformation(new Transformation(
|
||||||
|
displayData.getTranslation(),
|
||||||
|
new Quaternionf(),
|
||||||
|
displayData.getScale(),
|
||||||
|
new Quaternionf())
|
||||||
|
);
|
||||||
|
|
||||||
|
// entity shadow
|
||||||
|
display.setShadowRadius(displayData.getShadowRadius());
|
||||||
|
display.setShadowStrength(displayData.getShadowStrength());
|
||||||
|
|
||||||
|
// view range
|
||||||
|
display.setViewRange(displayData.getVisibilityDistance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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.getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
||||||
|
|
||||||
|
// TODO: cache player protocol version
|
||||||
|
// TODO: fix this
|
||||||
|
// 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.viewers.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.viewers.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 (!isViewer(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<DataValue<?>>();
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
for (final var item : ((Int2ObjectMap<DataItem<?>>) getValue(display.getEntityData(), "e")).values()) {
|
||||||
|
values.add(item.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
((CraftPlayer) player).getHandle().connection.send(new ClientboundSetEntityDataPacket(display.getId(), values));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
public enum MappingKeys1_19_4 {
|
||||||
|
|
||||||
|
DATA_INTERPOLATION_DURATION_ID("r"),
|
||||||
|
DATA_INTERPOLATION_START_DELTA_TICKS_ID("q"),
|
||||||
|
DATA_LINE_WIDTH_ID("aL"),
|
||||||
|
DATA_BACKGROUND_COLOR_ID("aM"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String mapping;
|
||||||
|
|
||||||
|
MappingKeys1_19_4(String mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapping() {
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("io.papermc.paperweight.userdev")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val minecraftVersion = "1.20.1"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:api"))
|
||||||
|
implementation("de.oliver:FancyLib:36")
|
||||||
|
compileOnly("com.viaversion:viaversion-api:5.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named("assemble") {
|
||||||
|
dependsOn(named("reobfJar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
|
||||||
|
options.release.set(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramHideEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancylib.ReflectionUtils;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
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.DataItem;
|
||||||
|
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.v1_20_R1.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R1.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_1 extends Hologram {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Display display;
|
||||||
|
|
||||||
|
public Hologram1_20_1(@NotNull final HologramData data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEntityId() {
|
||||||
|
return display.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable org.bukkit.entity.Display getDisplayEntity() {
|
||||||
|
return display != null ? (org.bukkit.entity.Display) display.getBukkitEntity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create() {
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (!location.isWorldLoaded()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData dd){
|
||||||
|
final var DATA_INTERPOLATION_DURATION_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_1.DATA_INTERPOLATION_DURATION_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_DURATION_ID, dd.getInterpolationDuration());
|
||||||
|
|
||||||
|
final var DATA_INTERPOLATION_START_DELTA_TICKS_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_1.DATA_INTERPOLATION_START_DELTA_TICKS_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_START_DELTA_TICKS_ID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.getLocation();
|
||||||
|
if (location.getWorld() == null || !location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
display.setPosRaw(location.x(), location.y(), location.z());
|
||||||
|
display.setYRot(location.getYaw());
|
||||||
|
display.setXRot(location.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display instanceof TextDisplay textDisplay && data instanceof TextHologramData textData) {
|
||||||
|
// line width
|
||||||
|
final var DATA_LINE_WIDTH_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_1.DATA_LINE_WIDTH_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_LINE_WIDTH_ID, Hologram.LINE_WIDTH);
|
||||||
|
|
||||||
|
// background
|
||||||
|
final var DATA_BACKGROUND_COLOR_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_1.DATA_BACKGROUND_COLOR_ID.getMapping()); //DATA_BACKGROUND_COLOR_ID
|
||||||
|
|
||||||
|
final var background = textData.getBackground();
|
||||||
|
if (background == null) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, TextDisplay.INITIAL_BACKGROUND);
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, 0);
|
||||||
|
} else {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
// text shadow
|
||||||
|
if (textData.hasTextShadow()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SHADOW));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SHADOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// see through
|
||||||
|
if (textData.isSeeThrough()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 instanceof ItemHologramData itemData) {
|
||||||
|
// item
|
||||||
|
itemDisplay.setItemStack(ItemStack.fromBukkitCopy(itemData.getItemStack()));
|
||||||
|
|
||||||
|
} else if (display instanceof Display.BlockDisplay blockDisplay && data instanceof BlockHologramData blockData) {
|
||||||
|
Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + blockData.getBlock().name().toLowerCase(), ':'));
|
||||||
|
blockDisplay.setBlockState(block.defaultBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
// billboard data
|
||||||
|
display.setBillboardConstraints(switch (displayData.getBillboard()) {
|
||||||
|
case FIXED -> Display.BillboardConstraints.FIXED;
|
||||||
|
case VERTICAL -> Display.BillboardConstraints.VERTICAL;
|
||||||
|
case HORIZONTAL -> Display.BillboardConstraints.HORIZONTAL;
|
||||||
|
case CENTER -> Display.BillboardConstraints.CENTER;
|
||||||
|
});
|
||||||
|
|
||||||
|
// brightness
|
||||||
|
if (displayData.getBrightness() != null) {
|
||||||
|
display.setBrightnessOverride(new Brightness(displayData.getBrightness().getBlockLight(), displayData.getBrightness().getSkyLight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity scale AND MORE!
|
||||||
|
display.setTransformation(new Transformation(
|
||||||
|
displayData.getTranslation(),
|
||||||
|
new Quaternionf(),
|
||||||
|
displayData.getScale(),
|
||||||
|
new Quaternionf())
|
||||||
|
);
|
||||||
|
|
||||||
|
// entity shadow
|
||||||
|
display.setShadowRadius(displayData.getShadowRadius());
|
||||||
|
display.setShadowStrength(displayData.getShadowStrength());
|
||||||
|
|
||||||
|
// view range
|
||||||
|
display.setViewRange(displayData.getVisibilityDistance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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.getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
||||||
|
|
||||||
|
// TODO: cache player protocol version
|
||||||
|
// TODO: fix this
|
||||||
|
// 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.viewers.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.viewers.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 (!isViewer(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<DataValue<?>>();
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
for (final var item : ((Int2ObjectMap<DataItem<?>>) getValue(display.getEntityData(), "e")).values()) {
|
||||||
|
values.add(item.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
((CraftPlayer) player).getHandle().connection.send(new ClientboundSetEntityDataPacket(display.getId(), values));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
public enum MappingKeys1_20_1 {
|
||||||
|
|
||||||
|
DATA_INTERPOLATION_DURATION_ID("q"),
|
||||||
|
DATA_INTERPOLATION_START_DELTA_TICKS_ID("p"),
|
||||||
|
DATA_LINE_WIDTH_ID("aM"),
|
||||||
|
DATA_BACKGROUND_COLOR_ID("aN"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String mapping;
|
||||||
|
|
||||||
|
MappingKeys1_20_1(String mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapping() {
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("io.papermc.paperweight.userdev")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val minecraftVersion = "1.20.2"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:api"))
|
||||||
|
implementation("de.oliver:FancyLib:36")
|
||||||
|
compileOnly("com.viaversion:viaversion-api:5.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named("assemble") {
|
||||||
|
dependsOn(named("reobfJar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
|
||||||
|
options.release.set(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramHideEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancylib.ReflectionUtils;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
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.DataItem;
|
||||||
|
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.v1_20_R2.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R2.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_2 extends Hologram {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Display display;
|
||||||
|
|
||||||
|
public Hologram1_20_2(@NotNull final HologramData data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEntityId() {
|
||||||
|
return display.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable org.bukkit.entity.Display getDisplayEntity() {
|
||||||
|
return display != null ? (org.bukkit.entity.Display) display.getBukkitEntity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create() {
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (!location.isWorldLoaded()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData dd) {
|
||||||
|
final var DATA_INTERPOLATION_DURATION_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_2.DATA_INTERPOLATION_DURATION_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_DURATION_ID, dd.getInterpolationDuration());
|
||||||
|
|
||||||
|
final var DATA_INTERPOLATION_START_DELTA_TICKS_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_2.DATA_INTERPOLATION_START_DELTA_TICKS_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_INTERPOLATION_START_DELTA_TICKS_ID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.getLocation();
|
||||||
|
if (location.getWorld() == null || !location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
display.setPosRaw(location.x(), location.y(), location.z());
|
||||||
|
display.setYRot(location.getYaw());
|
||||||
|
display.setXRot(location.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display instanceof TextDisplay textDisplay && data instanceof TextHologramData textData) {
|
||||||
|
// line width
|
||||||
|
final var DATA_LINE_WIDTH_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_2.DATA_LINE_WIDTH_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_LINE_WIDTH_ID, Hologram.LINE_WIDTH);
|
||||||
|
|
||||||
|
// background
|
||||||
|
final var DATA_BACKGROUND_COLOR_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_2.DATA_BACKGROUND_COLOR_ID.getMapping());
|
||||||
|
|
||||||
|
final var background = textData.getBackground();
|
||||||
|
if (background == null) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, TextDisplay.INITIAL_BACKGROUND);
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, 0);
|
||||||
|
} else {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
// text shadow
|
||||||
|
if (textData.hasTextShadow()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SHADOW));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SHADOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// see through
|
||||||
|
if (textData.isSeeThrough()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 instanceof ItemHologramData itemData) {
|
||||||
|
// item
|
||||||
|
itemDisplay.setItemStack(ItemStack.fromBukkitCopy(itemData.getItemStack()));
|
||||||
|
|
||||||
|
} else if (display instanceof Display.BlockDisplay blockDisplay && data instanceof BlockHologramData blockData) {
|
||||||
|
Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + blockData.getBlock().name().toLowerCase(), ':'));
|
||||||
|
blockDisplay.setBlockState(block.defaultBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
// billboard data
|
||||||
|
display.setBillboardConstraints(switch (displayData.getBillboard()) {
|
||||||
|
case FIXED -> Display.BillboardConstraints.FIXED;
|
||||||
|
case VERTICAL -> Display.BillboardConstraints.VERTICAL;
|
||||||
|
case HORIZONTAL -> Display.BillboardConstraints.HORIZONTAL;
|
||||||
|
case CENTER -> Display.BillboardConstraints.CENTER;
|
||||||
|
});
|
||||||
|
|
||||||
|
// brightness
|
||||||
|
if (displayData.getBrightness() != null) {
|
||||||
|
display.setBrightnessOverride(new Brightness(displayData.getBrightness().getBlockLight(), displayData.getBrightness().getSkyLight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity scale AND MORE!
|
||||||
|
display.setTransformation(new Transformation(
|
||||||
|
displayData.getTranslation(),
|
||||||
|
new Quaternionf(),
|
||||||
|
displayData.getScale(),
|
||||||
|
new Quaternionf())
|
||||||
|
);
|
||||||
|
|
||||||
|
// entity shadow
|
||||||
|
display.setShadowRadius(displayData.getShadowRadius());
|
||||||
|
display.setShadowStrength(displayData.getShadowStrength());
|
||||||
|
|
||||||
|
// view range
|
||||||
|
display.setViewRange(displayData.getVisibilityDistance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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.getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
||||||
|
|
||||||
|
// TODO: cache player protocol version
|
||||||
|
// TODO: fix this
|
||||||
|
// 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.viewers.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.viewers.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 (!isViewer(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<DataValue<?>>();
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
for (final var item : ((Int2ObjectMap<DataItem<?>>) getValue(display.getEntityData(), "e")).values()) {
|
||||||
|
values.add(item.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
((CraftPlayer) player).getHandle().connection.send(new ClientboundSetEntityDataPacket(display.getId(), values));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
public enum MappingKeys1_20_2 {
|
||||||
|
|
||||||
|
DATA_INTERPOLATION_DURATION_ID("r"),
|
||||||
|
DATA_INTERPOLATION_START_DELTA_TICKS_ID("q"),
|
||||||
|
DATA_LINE_WIDTH_ID("aN"),
|
||||||
|
DATA_BACKGROUND_COLOR_ID("aO"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String mapping;
|
||||||
|
|
||||||
|
MappingKeys1_20_2(String mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapping() {
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("java-library")
|
||||||
|
id("io.papermc.paperweight.userdev")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val minecraftVersion = "1.20.4"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(project(":plugins:fancyholograms-v2:api"))
|
||||||
|
implementation("de.oliver:FancyLib:36")
|
||||||
|
compileOnly("com.viaversion:viaversion-api:5.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named("assemble") {
|
||||||
|
dependsOn(named("reobfJar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = Charsets.UTF_8.name()
|
||||||
|
|
||||||
|
options.release.set(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramHideEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancylib.ReflectionUtils;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
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.DataItem;
|
||||||
|
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.v1_20_R3.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.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_4 extends Hologram {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Display display;
|
||||||
|
|
||||||
|
public Hologram1_20_4(@NotNull final HologramData data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEntityId() {
|
||||||
|
return display.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable org.bukkit.entity.Display getDisplayEntity() {
|
||||||
|
return display != null ? (org.bukkit.entity.Display) display.getBukkitEntity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create() {
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (!location.isWorldLoaded()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData dd) {
|
||||||
|
final var DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_4.DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID, dd.getInterpolationDuration());
|
||||||
|
|
||||||
|
final var DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_4.DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.getLocation();
|
||||||
|
if (location.getWorld() == null || !location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
display.setPosRaw(location.x(), location.y(), location.z());
|
||||||
|
display.setYRot(location.getYaw());
|
||||||
|
display.setXRot(location.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display instanceof TextDisplay textDisplay && data instanceof TextHologramData textData) {
|
||||||
|
// line width
|
||||||
|
final var DATA_LINE_WIDTH_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_4.TEXT_DISPLAY__DATA_LINE_WIDTH_ID.getMapping());
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_LINE_WIDTH_ID, Hologram.LINE_WIDTH);
|
||||||
|
|
||||||
|
// background
|
||||||
|
final var DATA_BACKGROUND_COLOR_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_4.TEXT_DISPLAY__DATA_BACKGROUND_COLOR_ID.getMapping());
|
||||||
|
|
||||||
|
final var background = textData.getBackground();
|
||||||
|
if (background == null) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, TextDisplay.INITIAL_BACKGROUND);
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, 0);
|
||||||
|
} else {
|
||||||
|
display.getEntityData().set((EntityDataAccessor<Integer>) DATA_BACKGROUND_COLOR_ID, background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
// text shadow
|
||||||
|
if (textData.hasTextShadow()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SHADOW));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SHADOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// see through
|
||||||
|
if (textData.isSeeThrough()) {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
} else {
|
||||||
|
textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SEE_THROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 instanceof ItemHologramData itemData) {
|
||||||
|
// item
|
||||||
|
itemDisplay.setItemStack(ItemStack.fromBukkitCopy(itemData.getItemStack()));
|
||||||
|
|
||||||
|
} else if (display instanceof Display.BlockDisplay blockDisplay && data instanceof BlockHologramData blockData) {
|
||||||
|
Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + blockData.getBlock().name().toLowerCase(), ':'));
|
||||||
|
blockDisplay.setBlockState(block.defaultBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
// billboard data
|
||||||
|
display.setBillboardConstraints(switch (displayData.getBillboard()) {
|
||||||
|
case FIXED -> Display.BillboardConstraints.FIXED;
|
||||||
|
case VERTICAL -> Display.BillboardConstraints.VERTICAL;
|
||||||
|
case HORIZONTAL -> Display.BillboardConstraints.HORIZONTAL;
|
||||||
|
case CENTER -> Display.BillboardConstraints.CENTER;
|
||||||
|
});
|
||||||
|
|
||||||
|
// brightness
|
||||||
|
if (displayData.getBrightness() != null) {
|
||||||
|
display.setBrightnessOverride(new Brightness(displayData.getBrightness().getBlockLight(), displayData.getBrightness().getSkyLight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity scale AND MORE!
|
||||||
|
display.setTransformation(new Transformation(
|
||||||
|
displayData.getTranslation(),
|
||||||
|
new Quaternionf(),
|
||||||
|
displayData.getScale(),
|
||||||
|
new Quaternionf())
|
||||||
|
);
|
||||||
|
|
||||||
|
// entity shadow
|
||||||
|
display.setShadowRadius(displayData.getShadowRadius());
|
||||||
|
display.setShadowStrength(displayData.getShadowStrength());
|
||||||
|
|
||||||
|
// view range
|
||||||
|
display.setViewRange(displayData.getVisibilityDistance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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.getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
||||||
|
|
||||||
|
// TODO: cache player protocol version
|
||||||
|
// TODO: fix this
|
||||||
|
// final var protocolVersion = FancyHologramsPlugin.get().isUsingViaVersion() ? Via.getAPI().getPlayerVersion(player.getUniqueId()) : MINIMUM_PROTOCOL_VERSION;
|
||||||
|
// if (protocolVersion < MINIMUM_PROTOCOL_VERSION) {
|
||||||
|
// System.out.println("nope protocol");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
serverPlayer.connection.send(new ClientboundAddEntityPacket(display));
|
||||||
|
this.viewers.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.viewers.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 (!isViewer(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<DataValue<?>>();
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
for (final var item : ((Int2ObjectMap<DataItem<?>>) getValue(display.getEntityData(), "e")).values()) {
|
||||||
|
values.add(item.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
((CraftPlayer) player).getHandle().connection.send(new ClientboundSetEntityDataPacket(display.getId(), values));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
public enum MappingKeys1_20_4 {
|
||||||
|
|
||||||
|
DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID("r"),
|
||||||
|
DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID("q"),
|
||||||
|
TEXT_DISPLAY__DATA_LINE_WIDTH_ID("aN"),
|
||||||
|
TEXT_DISPLAY__DATA_BACKGROUND_COLOR_ID("aO"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String mapping;
|
||||||
|
|
||||||
|
MappingKeys1_20_4(String mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapping() {
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package de.oliver.fancyholograms;
|
||||||
|
|
||||||
|
import de.oliver.fancylib.featureFlags.FeatureFlag;
|
||||||
|
import de.oliver.fancylib.featureFlags.FeatureFlagConfig;
|
||||||
|
|
||||||
|
public class FHFeatureFlags {
|
||||||
|
|
||||||
|
public static final FeatureFlag DISABLE_HOLOGRAMS_FOR_BEDROCK_PLAYERS = new FeatureFlag("disable-holograms-for-bedrock-players", "Do not show holograms to bedrock players", false);
|
||||||
|
|
||||||
|
public static void load() {
|
||||||
|
FeatureFlagConfig config = new FeatureFlagConfig(FancyHolograms.get());
|
||||||
|
config.addFeatureFlag(DISABLE_HOLOGRAMS_FOR_BEDROCK_PLAYERS);
|
||||||
|
config.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
package de.oliver.fancyholograms;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import de.oliver.fancyanalytics.api.FancyAnalyticsAPI;
|
||||||
|
import de.oliver.fancyanalytics.api.metrics.MetricSupplier;
|
||||||
|
import de.oliver.fancyanalytics.logger.ExtendedFancyLogger;
|
||||||
|
import de.oliver.fancyanalytics.logger.LogLevel;
|
||||||
|
import de.oliver.fancyanalytics.logger.appender.Appender;
|
||||||
|
import de.oliver.fancyanalytics.logger.appender.ConsoleAppender;
|
||||||
|
import de.oliver.fancyanalytics.logger.appender.JsonAppender;
|
||||||
|
import de.oliver.fancyholograms.api.FancyHologramsPlugin;
|
||||||
|
import de.oliver.fancyholograms.api.HologramConfiguration;
|
||||||
|
import de.oliver.fancyholograms.api.HologramManager;
|
||||||
|
import de.oliver.fancyholograms.api.HologramStorage;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.FancyHologramsCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.FancyHologramsTestCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.hologram.version.*;
|
||||||
|
import de.oliver.fancyholograms.listeners.BedrockPlayerListener;
|
||||||
|
import de.oliver.fancyholograms.listeners.NpcListener;
|
||||||
|
import de.oliver.fancyholograms.listeners.PlayerListener;
|
||||||
|
import de.oliver.fancyholograms.listeners.WorldListener;
|
||||||
|
import de.oliver.fancyholograms.storage.FlatFileHologramStorage;
|
||||||
|
import de.oliver.fancyholograms.storage.converter.FHConversionRegistry;
|
||||||
|
import de.oliver.fancyholograms.util.PluginUtils;
|
||||||
|
import de.oliver.fancylib.FancyLib;
|
||||||
|
import de.oliver.fancylib.Metrics;
|
||||||
|
import de.oliver.fancylib.VersionConfig;
|
||||||
|
import de.oliver.fancylib.serverSoftware.ServerSoftware;
|
||||||
|
import de.oliver.fancylib.versionFetcher.MasterVersionFetcher;
|
||||||
|
import de.oliver.fancylib.versionFetcher.VersionFetcher;
|
||||||
|
import de.oliver.fancysitula.api.IFancySitula;
|
||||||
|
import de.oliver.fancysitula.api.utils.ServerVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static java.util.concurrent.CompletableFuture.supplyAsync;
|
||||||
|
|
||||||
|
public final class FancyHolograms extends JavaPlugin implements FancyHologramsPlugin {
|
||||||
|
|
||||||
|
private static @Nullable FancyHolograms INSTANCE;
|
||||||
|
private final ExtendedFancyLogger fancyLogger;
|
||||||
|
private final VersionFetcher versionFetcher = new MasterVersionFetcher("FancyHolograms");
|
||||||
|
private final VersionConfig versionConfig = new VersionConfig(this, versionFetcher);
|
||||||
|
private final ScheduledExecutorService hologramThread = Executors.newSingleThreadScheduledExecutor(
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("FancyHolograms-Holograms")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
private final ExecutorService fileStorageExecutor = Executors.newSingleThreadExecutor(
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setDaemon(true)
|
||||||
|
.setPriority(Thread.MIN_PRIORITY + 1)
|
||||||
|
.setNameFormat("FancyHolograms-FileStorageExecutor")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
private FancyAnalyticsAPI fancyAnalytics;
|
||||||
|
private HologramConfiguration configuration = new FancyHologramsConfiguration();
|
||||||
|
private HologramStorage hologramStorage = new FlatFileHologramStorage();
|
||||||
|
private @Nullable HologramManagerImpl hologramsManager;
|
||||||
|
|
||||||
|
public FancyHolograms() {
|
||||||
|
INSTANCE = this;
|
||||||
|
|
||||||
|
Appender consoleAppender = new ConsoleAppender("[{loggerName}] ({threadName}) {logLevel}: {message}");
|
||||||
|
String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date(System.currentTimeMillis()));
|
||||||
|
File logsFile = new File("plugins/FancyHolograms/logs/FH-logs-" + date + ".txt");
|
||||||
|
if (!logsFile.exists()) {
|
||||||
|
try {
|
||||||
|
logsFile.getParentFile().mkdirs();
|
||||||
|
logsFile.createNewFile();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonAppender jsonAppender = new JsonAppender(false, false, true, logsFile.getPath());
|
||||||
|
this.fancyLogger = new ExtendedFancyLogger("FancyHolograms", LogLevel.INFO, List.of(consoleAppender, jsonAppender), new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull FancyHolograms get() {
|
||||||
|
return Objects.requireNonNull(INSTANCE, "plugin is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canGet() {
|
||||||
|
return INSTANCE != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
final var adapter = resolveHologramAdapter();
|
||||||
|
|
||||||
|
if (adapter == null) {
|
||||||
|
List<String> supportedVersions = new ArrayList<>(List.of("1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4"));
|
||||||
|
supportedVersions.addAll(ServerVersion.getSupportedVersions());
|
||||||
|
|
||||||
|
fancyLogger.warn("""
|
||||||
|
--------------------------------------------------
|
||||||
|
Unsupported minecraft server version.
|
||||||
|
Please update the server to one of (%s).
|
||||||
|
Disabling the FancyHolograms plugin.
|
||||||
|
--------------------------------------------------
|
||||||
|
""".formatted(String.join(" / ", supportedVersions)));
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hologramsManager = new HologramManagerImpl(this, adapter);
|
||||||
|
|
||||||
|
fancyLogger.info("Successfully loaded FancyHolograms version %s".formatted(getDescription().getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
getHologramConfiguration().reload(this); // initialize configuration
|
||||||
|
|
||||||
|
new FancyLib(INSTANCE); // initialize FancyLib
|
||||||
|
|
||||||
|
if (!ServerSoftware.isPaper()) {
|
||||||
|
fancyLogger.warn("""
|
||||||
|
--------------------------------------------------
|
||||||
|
It is recommended to use Paper as server software.
|
||||||
|
Because you are not using paper, the plugin
|
||||||
|
might not work correctly.
|
||||||
|
--------------------------------------------------
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
LogLevel logLevel;
|
||||||
|
try {
|
||||||
|
logLevel = LogLevel.valueOf(getHologramConfiguration().getLogLevel());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logLevel = LogLevel.INFO;
|
||||||
|
}
|
||||||
|
fancyLogger.setCurrentLevel(logLevel);
|
||||||
|
IFancySitula.LOGGER.setCurrentLevel(logLevel);
|
||||||
|
|
||||||
|
FHFeatureFlags.load();
|
||||||
|
|
||||||
|
reloadCommands();
|
||||||
|
|
||||||
|
registerListeners();
|
||||||
|
|
||||||
|
versionConfig.load();
|
||||||
|
if (!getHologramConfiguration().areVersionNotificationsMuted()) {
|
||||||
|
checkForNewerVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerMetrics();
|
||||||
|
|
||||||
|
getHologramsManager().initializeTasks();
|
||||||
|
|
||||||
|
if (getHologramConfiguration().isAutosaveEnabled()) {
|
||||||
|
getHologramThread().scheduleAtFixedRate(() -> {
|
||||||
|
if (hologramsManager != null) {
|
||||||
|
hologramsManager.saveHolograms();
|
||||||
|
}
|
||||||
|
}, getHologramConfiguration().getAutosaveInterval(), getHologramConfiguration().getAutosaveInterval() * 60L, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
FHConversionRegistry.registerBuiltInConverters();
|
||||||
|
|
||||||
|
fancyLogger.info("Successfully enabled FancyHolograms version %s".formatted(getDescription().getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
hologramsManager.saveHolograms();
|
||||||
|
hologramThread.shutdown();
|
||||||
|
fileStorageExecutor.shutdown();
|
||||||
|
INSTANCE = null;
|
||||||
|
|
||||||
|
fancyLogger.info("Successfully disabled FancyHolograms version %s".formatted(getDescription().getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaPlugin getPlugin() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtendedFancyLogger getFancyLogger() {
|
||||||
|
return fancyLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull VersionFetcher getVersionFetcher() {
|
||||||
|
return versionFetcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull VersionConfig getVersionConfig() {
|
||||||
|
return versionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public @NotNull HologramManagerImpl getHologramsManager() {
|
||||||
|
return Objects.requireNonNull(this.hologramsManager, "plugin is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HologramManager getHologramManager() {
|
||||||
|
return Objects.requireNonNull(this.hologramsManager, "plugin is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HologramConfiguration getHologramConfiguration() {
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHologramConfiguration(HologramConfiguration configuration, boolean reload) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
configuration.reload(this);
|
||||||
|
reloadCommands();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HologramStorage getHologramStorage() {
|
||||||
|
return hologramStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHologramStorage(HologramStorage storage, boolean reload) {
|
||||||
|
this.hologramStorage = storage;
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
getHologramsManager().reloadHolograms();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduledExecutorService getHologramThread() {
|
||||||
|
return hologramThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExecutorService getFileStorageExecutor() {
|
||||||
|
return this.fileStorageExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Function<HologramData, Hologram> resolveHologramAdapter() {
|
||||||
|
final var version = Bukkit.getMinecraftVersion();
|
||||||
|
|
||||||
|
// check if the server version is supported by FancySitula
|
||||||
|
if (ServerVersion.isVersionSupported(version)) {
|
||||||
|
return HologramImpl::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (version) {
|
||||||
|
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;
|
||||||
|
case "1.19.4" -> Hologram1_19_4::new;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadCommands() {
|
||||||
|
Collection<Command> commands = Arrays.asList(new HologramCMD(this), new FancyHologramsCMD(this));
|
||||||
|
|
||||||
|
if (getHologramConfiguration().isRegisterCommands()) {
|
||||||
|
commands.forEach(command -> getServer().getCommandMap().register("fancyholograms", command));
|
||||||
|
} else {
|
||||||
|
commands.stream().filter(Command::isRegistered).forEach(command ->
|
||||||
|
command.unregister(getServer().getCommandMap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
FancyHologramsTestCMD fancyHologramsTestCMD = new FancyHologramsTestCMD(this);
|
||||||
|
getServer().getCommandMap().register("fancyholograms", fancyHologramsTestCMD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerListeners() {
|
||||||
|
getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
|
||||||
|
getServer().getPluginManager().registerEvents(new WorldListener(), this);
|
||||||
|
|
||||||
|
if (PluginUtils.isFancyNpcsEnabled()) {
|
||||||
|
getServer().getPluginManager().registerEvents(new NpcListener(this), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FHFeatureFlags.DISABLE_HOLOGRAMS_FOR_BEDROCK_PLAYERS.isEnabled() && PluginUtils.isFloodgateEnabled()) {
|
||||||
|
getServer().getPluginManager().registerEvents(new BedrockPlayerListener(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForNewerVersion() {
|
||||||
|
final var current = new ComparableVersion(versionConfig.getVersion());
|
||||||
|
|
||||||
|
supplyAsync(getVersionFetcher()::fetchNewestVersion).thenApply(Objects::requireNonNull).whenComplete((newest, error) -> {
|
||||||
|
if (error != null || newest.compareTo(current) <= 0) {
|
||||||
|
return; // could not get the newest version or already on latest
|
||||||
|
}
|
||||||
|
|
||||||
|
fancyLogger.warn("""
|
||||||
|
|
||||||
|
-------------------------------------------------------
|
||||||
|
You are not using the latest version of the FancyHolograms plugin.
|
||||||
|
Please update to the newest version (%s).
|
||||||
|
%s
|
||||||
|
-------------------------------------------------------
|
||||||
|
""".formatted(newest, getVersionFetcher().getDownloadUrl()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerMetrics() {
|
||||||
|
boolean isDevelopmentBuild = !versionConfig.getBuild().equalsIgnoreCase("undefined");
|
||||||
|
|
||||||
|
Metrics metrics = new Metrics(this, 17990);
|
||||||
|
metrics.addCustomChart(new Metrics.SingleLineChart("total_holograms", () -> hologramsManager.getHolograms().size()));
|
||||||
|
metrics.addCustomChart(new Metrics.SimplePie("update_notifications", () -> configuration.areVersionNotificationsMuted() ? "No" : "Yes"));
|
||||||
|
metrics.addCustomChart(new Metrics.SimplePie("using_development_build", () -> isDevelopmentBuild ? "Yes" : "No"));
|
||||||
|
|
||||||
|
fancyAnalytics = new FancyAnalyticsAPI("3b77bd59-2b01-46f2-b3aa-a9584401797f", "E2gW5zc2ZTk1OGFkNGY2ZDQ0ODlM6San");
|
||||||
|
fancyAnalytics.getConfig().setDisableLogging(true);
|
||||||
|
|
||||||
|
if (!isDevelopmentBuild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fancyAnalytics.registerMinecraftPluginMetrics(INSTANCE);
|
||||||
|
fancyAnalytics.getExceptionHandler().registerLogger(getLogger());
|
||||||
|
fancyAnalytics.getExceptionHandler().registerLogger(Bukkit.getLogger());
|
||||||
|
fancyAnalytics.getExceptionHandler().registerLogger(fancyLogger);
|
||||||
|
|
||||||
|
fancyAnalytics.registerStringMetric(new MetricSupplier<>("commit_hash", () -> versionConfig.getHash().substring(0, 7)));
|
||||||
|
|
||||||
|
fancyAnalytics.registerStringMetric(new MetricSupplier<>("server_size", () -> {
|
||||||
|
long onlinePlayers = Bukkit.getOnlinePlayers().size();
|
||||||
|
|
||||||
|
if (onlinePlayers == 0) {
|
||||||
|
return "empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlinePlayers <= 25) {
|
||||||
|
return "small";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlinePlayers <= 100) {
|
||||||
|
return "medium";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlinePlayers <= 500) {
|
||||||
|
return "large";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "very_large";
|
||||||
|
}));
|
||||||
|
|
||||||
|
fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_holograms", () -> (double) hologramsManager.getHolograms().size()));
|
||||||
|
fancyAnalytics.registerStringMetric(new MetricSupplier<>("enabled_update_notifications", () -> configuration.areVersionNotificationsMuted() ? "false" : "true"));
|
||||||
|
fancyAnalytics.registerStringMetric(new MetricSupplier<>("fflag_disable_holograms_for_bedrock_players", () -> FHFeatureFlags.DISABLE_HOLOGRAMS_FOR_BEDROCK_PLAYERS.isEnabled() ? "true" : "false"));
|
||||||
|
fancyAnalytics.registerStringMetric(new MetricSupplier<>("using_development_build", () -> isDevelopmentBuild ? "true" : "false"));
|
||||||
|
|
||||||
|
fancyAnalytics.registerStringArrayMetric(new MetricSupplier<>("hologram_type", () -> {
|
||||||
|
if (hologramsManager == null) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hologramsManager.getHolograms().stream()
|
||||||
|
.map(h -> h.getData().getType().name())
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
fancyAnalytics.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FancyAnalyticsAPI getFancyAnalytics() {
|
||||||
|
return fancyAnalytics;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package de.oliver.fancyholograms;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.FancyHologramsPlugin;
|
||||||
|
import de.oliver.fancyholograms.api.HologramConfiguration;
|
||||||
|
import de.oliver.fancylib.ConfigHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FancyHologramsConfig class is responsible for managing the configuration of the FancyHolograms plugin.
|
||||||
|
* It handles loading and saving hologram data, as well as providing access to various configuration settings.
|
||||||
|
*/
|
||||||
|
public final class FancyHologramsConfiguration implements HologramConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether version notifications are muted.
|
||||||
|
*/
|
||||||
|
private boolean versionNotifsMuted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether autosave is enabled.
|
||||||
|
*/
|
||||||
|
private boolean autosaveEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval at which autosave is performed.
|
||||||
|
*/
|
||||||
|
private int autosaveInterval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the plugin should save holograms when they are changed.
|
||||||
|
*/
|
||||||
|
private boolean saveOnChangedEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default visibility distance for holograms.
|
||||||
|
*/
|
||||||
|
private int defaultVisibilityDistance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether commands should be registered.
|
||||||
|
* <p>
|
||||||
|
* This is useful for users who want to use the plugin's API only.
|
||||||
|
*/
|
||||||
|
private boolean registerCommands;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The log level for the plugin.
|
||||||
|
*/
|
||||||
|
private String logLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval at which hologram visibility is updated.
|
||||||
|
*/
|
||||||
|
private int updateVisibilityInterval;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reload(@NotNull FancyHologramsPlugin plugin) {
|
||||||
|
FancyHolograms pluginImpl = (FancyHolograms) plugin;
|
||||||
|
pluginImpl.reloadConfig();
|
||||||
|
|
||||||
|
final var config = pluginImpl.getConfig();
|
||||||
|
|
||||||
|
versionNotifsMuted = (boolean) ConfigHelper.getOrDefault(config, "mute_version_notification", false);
|
||||||
|
config.setInlineComments("mute_version_notification", List.of("Whether version notifications are muted."));
|
||||||
|
|
||||||
|
autosaveEnabled = (boolean) ConfigHelper.getOrDefault(config, "enable_autosave", true);
|
||||||
|
config.setInlineComments("enable_autosave", List.of("Whether autosave is enabled."));
|
||||||
|
|
||||||
|
autosaveInterval = (int) ConfigHelper.getOrDefault(config, "autosave_interval", 15);
|
||||||
|
config.setInlineComments("autosave_interval", List.of("The interval at which autosave is performed in minutes."));
|
||||||
|
|
||||||
|
saveOnChangedEnabled = (boolean) ConfigHelper.getOrDefault(config, "save_on_changed", true);
|
||||||
|
config.setInlineComments("save_on_changed", List.of("Whether the plugin should save holograms when they are changed."));
|
||||||
|
|
||||||
|
defaultVisibilityDistance = (int) ConfigHelper.getOrDefault(config, "visibility_distance", 20);
|
||||||
|
config.setInlineComments("visibility_distance", List.of("The default visibility distance for holograms."));
|
||||||
|
|
||||||
|
registerCommands = (boolean) ConfigHelper.getOrDefault(config, "register_commands", true);
|
||||||
|
config.setInlineComments("register_commands", List.of("Whether the plugin should register its commands."));
|
||||||
|
|
||||||
|
config.set("report_errors_to_sentry", null);
|
||||||
|
config.setInlineComments("report_errors_to_sentry", null);
|
||||||
|
|
||||||
|
config.setInlineComments("log_level", List.of("The log level for the plugin (DEBUG, INFO, WARN, ERROR)."));
|
||||||
|
logLevel = (String) ConfigHelper.getOrDefault(config, "log_level", "INFO");
|
||||||
|
|
||||||
|
updateVisibilityInterval = (int) ConfigHelper.getOrDefault(config, "update_visibility_interval", 20);
|
||||||
|
config.setInlineComments("update_visibility_interval", List.of("The interval at which hologram visibility is updated in ticks."));
|
||||||
|
|
||||||
|
if (pluginImpl.isEnabled()) {
|
||||||
|
plugin.getHologramThread().submit(pluginImpl::saveConfig);
|
||||||
|
} else {
|
||||||
|
// Can't dispatch task if plugin is disabled
|
||||||
|
pluginImpl.saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areVersionNotificationsMuted() {
|
||||||
|
return versionNotifsMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutosaveEnabled() {
|
||||||
|
return autosaveEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAutosaveInterval() {
|
||||||
|
return autosaveInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSaveOnChangedEnabled() {
|
||||||
|
return saveOnChangedEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultVisibilityDistance() {
|
||||||
|
return defaultVisibilityDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegisterCommands() {
|
||||||
|
return registerCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogLevel() {
|
||||||
|
return logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getUpdateVisibilityInterval() {
|
||||||
|
return updateVisibilityInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
package de.oliver.fancyholograms;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import de.oliver.fancyholograms.api.HologramManager;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramsLoadedEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramsUnloadedEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancynpcs.api.FancyNpcsPlugin;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.UnmodifiableView;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FancyHologramsManager class is responsible for managing holograms in the FancyHolograms plugin.
|
||||||
|
* It provides methods for adding, removing, and retrieving holograms, as well as other related operations.
|
||||||
|
*/
|
||||||
|
public final class HologramManagerImpl implements HologramManager {
|
||||||
|
|
||||||
|
private final @NotNull FancyHolograms plugin;
|
||||||
|
/**
|
||||||
|
* The adapter function used to create holograms from hologram data.
|
||||||
|
*/
|
||||||
|
private final @NotNull Function<HologramData, Hologram> adapter;
|
||||||
|
/**
|
||||||
|
* A map of hologram names to their corresponding hologram instances.
|
||||||
|
*/
|
||||||
|
private final Map<String, Hologram> holograms = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* Whether holograms are loaded or not
|
||||||
|
*/
|
||||||
|
private boolean isLoaded = false;
|
||||||
|
|
||||||
|
HologramManagerImpl(@NotNull final FancyHolograms plugin, @NotNull final Function<HologramData, Hologram> adapter) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A read-only collection of loaded holograms.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public @NotNull
|
||||||
|
@UnmodifiableView Collection<Hologram> getHolograms() {
|
||||||
|
return Collections.unmodifiableCollection(this.holograms.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a read-only view of the currently loaded persistent holograms.
|
||||||
|
*
|
||||||
|
* @return A read-only collection of holograms.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public @NotNull
|
||||||
|
@UnmodifiableView Collection<Hologram> getPersistentHolograms() {
|
||||||
|
return this.holograms.values().stream().filter(hologram -> hologram.getData().isPersistent()).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a hologram by name.
|
||||||
|
*
|
||||||
|
* @param name The name of the hologram to lookup.
|
||||||
|
* @return An optional containing the found hologram, or empty if not found.
|
||||||
|
*/
|
||||||
|
public @NotNull Optional<Hologram> getHologram(@NotNull final String name) {
|
||||||
|
return Optional.ofNullable(this.holograms.get(name.toLowerCase(Locale.ROOT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a hologram to this manager.
|
||||||
|
*
|
||||||
|
* @param hologram The hologram to add.
|
||||||
|
*/
|
||||||
|
public void addHologram(@NotNull final Hologram hologram) {
|
||||||
|
this.holograms.put(hologram.getData().getName().toLowerCase(Locale.ROOT), hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a hologram from this manager.
|
||||||
|
*
|
||||||
|
* @param hologram The hologram to remove.
|
||||||
|
*/
|
||||||
|
public void removeHologram(@NotNull final Hologram hologram) {
|
||||||
|
removeHologram(hologram.getData().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a hologram from this manager by name.
|
||||||
|
*
|
||||||
|
* @param name The name of the hologram to remove.
|
||||||
|
* @return An optional containing the removed hologram, or empty if not found.
|
||||||
|
*/
|
||||||
|
public @NotNull Optional<Hologram> removeHologram(@NotNull final String name) {
|
||||||
|
Optional<Hologram> optionalHologram = Optional.ofNullable(this.holograms.remove(name.toLowerCase(Locale.ROOT)));
|
||||||
|
|
||||||
|
optionalHologram.ifPresent(hologram -> {
|
||||||
|
for (UUID viewer : hologram.getViewers()) {
|
||||||
|
Player player = Bukkit.getPlayer(viewer);
|
||||||
|
if (player != null) {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> hologram.forceHideHologram(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> plugin.getHologramStorage().delete(hologram));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return optionalHologram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new hologram with the specified hologram data.
|
||||||
|
*
|
||||||
|
* @param data The hologram data for the new hologram.
|
||||||
|
* @return The created hologram.
|
||||||
|
*/
|
||||||
|
public @NotNull Hologram create(@NotNull final HologramData data) {
|
||||||
|
Hologram hologram = this.adapter.apply(data);
|
||||||
|
hologram.createHologram();
|
||||||
|
return hologram;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveHolograms() {
|
||||||
|
if (!isLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getHologramStorage().saveBatch(getPersistentHolograms(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadHolograms() {
|
||||||
|
List<Hologram> allLoaded = new ArrayList<>();
|
||||||
|
|
||||||
|
for (World world : Bukkit.getWorlds()) {
|
||||||
|
Collection<Hologram> loaded = plugin.getHologramStorage().loadAll(world.getName());
|
||||||
|
loaded.forEach(this::addHologram);
|
||||||
|
|
||||||
|
allLoaded.addAll(loaded);
|
||||||
|
}
|
||||||
|
isLoaded = true;
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> Bukkit.getPluginManager().callEvent(new HologramsLoadedEvent(ImmutableList.copyOf(allLoaded))));
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().info(String.format("Loaded %d holograms for all loaded worlds", allLoaded.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadHolograms(String world) {
|
||||||
|
ImmutableList<Hologram> loaded = ImmutableList.copyOf(plugin.getHologramStorage().loadAll(world));
|
||||||
|
loaded.forEach(this::addHologram);
|
||||||
|
|
||||||
|
isLoaded = true;
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().callEvent(new HologramsLoadedEvent(ImmutableList.copyOf(loaded)));
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().info(String.format("Loaded %d holograms for world %s", loaded.size(), world));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes tasks for managing holograms, such as loading and refreshing them.
|
||||||
|
* <p>
|
||||||
|
* This method is intended to be called internally by the plugin.
|
||||||
|
*/
|
||||||
|
void initializeTasks() {
|
||||||
|
ScheduledExecutorService hologramThread = plugin.getHologramThread();
|
||||||
|
hologramThread.submit(() -> {
|
||||||
|
loadHolograms();
|
||||||
|
|
||||||
|
hologramThread.scheduleAtFixedRate(() -> {
|
||||||
|
for (final Hologram hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
for (final Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
hologram.forceUpdateShownStateFor(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, plugin.getHologramConfiguration().getUpdateVisibilityInterval() * 50, TimeUnit.MILLISECONDS);
|
||||||
|
});
|
||||||
|
|
||||||
|
final var updateTimes = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
|
.<String, Long>build();
|
||||||
|
|
||||||
|
hologramThread.scheduleAtFixedRate(() -> {
|
||||||
|
final var time = System.currentTimeMillis();
|
||||||
|
|
||||||
|
for (final var hologram : getHolograms()) {
|
||||||
|
HologramData data = hologram.getData();
|
||||||
|
if (data.hasChanges()) {
|
||||||
|
hologram.forceUpdate();
|
||||||
|
hologram.refreshForViewersInWorld();
|
||||||
|
data.setHasChanges(false);
|
||||||
|
|
||||||
|
if (data instanceof TextHologramData) {
|
||||||
|
updateTimes.put(hologram.getData().getName(), time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50, 1000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
hologramThread.scheduleWithFixedDelay(() -> {
|
||||||
|
final var time = System.currentTimeMillis();
|
||||||
|
|
||||||
|
for (final var hologram : getHolograms()) {
|
||||||
|
if (hologram.getData() instanceof TextHologramData textData) {
|
||||||
|
final var interval = textData.getTextUpdateInterval();
|
||||||
|
if (interval < 1) {
|
||||||
|
continue; // doesn't update
|
||||||
|
}
|
||||||
|
|
||||||
|
final var lastUpdate = updateTimes.asMap().get(textData.getName());
|
||||||
|
if (lastUpdate != null && time < (lastUpdate + interval)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastUpdate == null || time > (lastUpdate + interval)) {
|
||||||
|
hologram.refreshForViewersInWorld();
|
||||||
|
updateTimes.put(textData.getName(), time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50, 50, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads holograms by clearing the existing holograms and loading them again from the plugin's configuration.
|
||||||
|
*/
|
||||||
|
public void reloadHolograms() {
|
||||||
|
unloadHolograms();
|
||||||
|
loadHolograms();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unloadHolograms() {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
List<Hologram> unloaded = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final var hologram : this.getPersistentHolograms()) {
|
||||||
|
this.holograms.remove(hologram.getName());
|
||||||
|
unloaded.add(hologram);
|
||||||
|
|
||||||
|
for (UUID viewer : hologram.getViewers()) {
|
||||||
|
Player player = Bukkit.getPlayer(viewer);
|
||||||
|
if (player != null) {
|
||||||
|
hologram.forceHideHologram(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().callEvent(new HologramsUnloadedEvent(ImmutableList.copyOf(unloaded)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unloadHolograms(String world) {
|
||||||
|
final var online = List.copyOf(Bukkit.getOnlinePlayers());
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
List<Hologram> h = getPersistentHolograms().stream()
|
||||||
|
.filter(hologram -> hologram.getData().getLocation().getWorld().getName().equals(world))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramStorage().saveBatch(h, false);
|
||||||
|
|
||||||
|
for (final Hologram hologram : h) {
|
||||||
|
this.holograms.remove(hologram.getName());
|
||||||
|
online.forEach(hologram::forceHideHologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().callEvent(new HologramsUnloadedEvent(ImmutableList.copyOf(h)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syncs a hologram with its linked NPC, if any.
|
||||||
|
*
|
||||||
|
* @param hologram The hologram to sync.
|
||||||
|
*/
|
||||||
|
public void syncHologramWithNpc(@NotNull final Hologram hologram) {
|
||||||
|
final var linkedNpcName = hologram.getData().getLinkedNpcName();
|
||||||
|
if (linkedNpcName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var npc = FancyNpcsPlugin.get().getNpcManager().getNpc(linkedNpcName);
|
||||||
|
if (npc == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
npc.getData().setDisplayName("<empty>");
|
||||||
|
npc.getData().setShowInTab(false);
|
||||||
|
npc.updateForAll();
|
||||||
|
|
||||||
|
final var npcScale = npc.getData().getScale();
|
||||||
|
|
||||||
|
if (hologram.getData() instanceof DisplayHologramData displayData) {
|
||||||
|
displayData.setScale(new Vector3f(npcScale));
|
||||||
|
}
|
||||||
|
|
||||||
|
final var location = npc.getData().getLocation().clone().add(0, (npc.getEyeHeight() * npcScale) + (0.5 * npcScale), 0);
|
||||||
|
hologram.getData().setLocation(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package de.oliver.fancyholograms.commands;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.storage.converter.ConverterTarget;
|
||||||
|
import de.oliver.fancyholograms.storage.converter.FHConversionRegistry;
|
||||||
|
import de.oliver.fancyholograms.storage.converter.HologramConversionSession;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancylib.translations.message.Message;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public final class FancyHologramsCMD extends Command {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final FancyHolograms plugin;
|
||||||
|
|
||||||
|
public FancyHologramsCMD(@NotNull final FancyHolograms plugin) {
|
||||||
|
super("fancyholograms");
|
||||||
|
setPermission("fancyholograms.admin");
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
|
||||||
|
if (!testPermission(sender)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 1) {
|
||||||
|
MessageHelper.info(sender, Constants.FH_COMMAND_USAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0].toLowerCase(Locale.ROOT)) {
|
||||||
|
case "save" -> {
|
||||||
|
this.plugin.getHologramsManager().saveHolograms();
|
||||||
|
MessageHelper.success(sender, "Saved all holograms");
|
||||||
|
}
|
||||||
|
case "reload" -> {
|
||||||
|
this.plugin.getHologramConfiguration().reload(plugin);
|
||||||
|
this.plugin.getHologramsManager().reloadHolograms();
|
||||||
|
this.plugin.reloadCommands();
|
||||||
|
|
||||||
|
MessageHelper.success(sender, "Reloaded config and holograms");
|
||||||
|
}
|
||||||
|
case "version" -> {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
FancyHolograms.get().getVersionConfig().checkVersionAndDisplay(sender, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case "convert" -> {
|
||||||
|
if (args.length < 3) {
|
||||||
|
MessageHelper.info(sender, "Usage: /fancyholograms convert <type> <targets> [args...]");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String converterId = args[1];
|
||||||
|
FHConversionRegistry.getConverterById(converterId)
|
||||||
|
.ifPresentOrElse((converter) -> {
|
||||||
|
final String[] converterArgs = Arrays.asList(args)
|
||||||
|
.subList(2, args.length)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
|
||||||
|
final ConverterTarget target = ConverterTarget.ofStringNullable(args[2]);
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
MessageHelper.error(sender, "Invalid regex for your conversion target!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HologramConversionSession session = new HologramConversionSession(target, sender, converterArgs);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final List<HologramData> holograms = converter.convert(session);
|
||||||
|
|
||||||
|
for (final HologramData data : holograms) {
|
||||||
|
final Hologram hologram = this.plugin.getHologramsManager().create(data);
|
||||||
|
this.plugin.getHologramsManager().addHologram(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.plugin.getHologramsManager().saveHolograms();
|
||||||
|
// TODO(matt): Give options to delete them or teleport and a list of IDs please
|
||||||
|
|
||||||
|
MessageHelper.success(sender, String.format("Converted successfully, produced %s total holograms!", holograms.size()));
|
||||||
|
} catch (Exception error) {
|
||||||
|
MessageHelper.error(sender, error.getMessage());
|
||||||
|
}
|
||||||
|
}, () -> MessageHelper.error(sender, "That converter is not registered. Look at the developer documentation if you are adding converters."));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
MessageHelper.info(sender, Constants.FH_COMMAND_USAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) throws IllegalArgumentException {
|
||||||
|
if (args.length < 1) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> suggestions = new ArrayList<>();
|
||||||
|
|
||||||
|
if (args.length == 1) {
|
||||||
|
suggestions.addAll(Arrays.asList("version", "reload", "save", "convert"));
|
||||||
|
} else {
|
||||||
|
if (Objects.equals(args[0], "convert")) {
|
||||||
|
|
||||||
|
if (args.length == 2) {
|
||||||
|
suggestions.addAll(FHConversionRegistry.getAllUsableConverterIds());
|
||||||
|
} else if (args.length == 3) {
|
||||||
|
final String converterId = args[1];
|
||||||
|
FHConversionRegistry.getConverterById(converterId)
|
||||||
|
.ifPresent((converter) -> {
|
||||||
|
suggestions.addAll(converter.getConvertableHolograms());
|
||||||
|
suggestions.add("*");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String lastArgument = args[args.length - 1];
|
||||||
|
|
||||||
|
return suggestions.stream()
|
||||||
|
.filter(alias -> alias.startsWith(lastArgument.toLowerCase(Locale.ROOT)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package de.oliver.fancyholograms.commands;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Color;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.TextDisplay;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FancyHologramsTestCMD extends Command {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final FancyHolograms plugin;
|
||||||
|
|
||||||
|
public FancyHologramsTestCMD(@NotNull final FancyHolograms plugin) {
|
||||||
|
super("FancyHologramsTest");
|
||||||
|
setPermission("fancyholograms.admin");
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
|
||||||
|
if (args.length == 1) {
|
||||||
|
return Arrays.asList("spawn100", "test1");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
|
||||||
|
if (!testPermission(sender)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player p)) {
|
||||||
|
MessageHelper.error(sender, "Only players can use this command!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 1 && args[0].equalsIgnoreCase("spawn100")) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
for (int j = 0; j < 10; j++) {
|
||||||
|
int n = (i * 10 + j) + 1;
|
||||||
|
TextHologramData textData = new TextHologramData("holo-" + n, p.getLocation().clone().add(5 * i + 1, 0, 5 * j + 1));
|
||||||
|
textData.setText(Arrays.asList(
|
||||||
|
"<rainbow><b>This is a test hologram! (#" + n + ")</b></rainbow>",
|
||||||
|
"<red>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<green>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<yellow>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<gradient:red:green>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<gradient:green:yellow>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris."
|
||||||
|
));
|
||||||
|
textData.setTextUpdateInterval(100)
|
||||||
|
.setScale(new Vector3f(.5f, .5f, .5f))
|
||||||
|
.setVisibilityDistance(100);
|
||||||
|
|
||||||
|
Hologram hologram = this.plugin.getHologramsManager().create(textData);
|
||||||
|
hologram.createHologram();
|
||||||
|
hologram.updateShownStateFor(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (args.length == 1 && args[0].equalsIgnoreCase("test1")) {
|
||||||
|
TextHologramData textData = new TextHologramData("holo-test1", p.getLocation());
|
||||||
|
textData.setText(Arrays.asList(
|
||||||
|
"<rainbow><b>This is a test hologram!</b></rainbow>",
|
||||||
|
"<red>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<green>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<yellow>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<gradient:red:green>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris.",
|
||||||
|
"<gradient:green:yellow>Lorem ipsum dolor sit amet, consec tetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris."
|
||||||
|
))
|
||||||
|
.setTextUpdateInterval(100)
|
||||||
|
.setTextAlignment(TextDisplay.TextAlignment.CENTER)
|
||||||
|
.setBackground(Color.fromARGB(15, 78, 237, 176))
|
||||||
|
.setTextShadow(true)
|
||||||
|
.setScale(new Vector3f(2, 2, 2))
|
||||||
|
.setBillboard(Display.Billboard.CENTER)
|
||||||
|
.setBrightness(new Display.Brightness(15, 15))
|
||||||
|
.setShadowRadius(3)
|
||||||
|
.setShadowStrength(3)
|
||||||
|
.setVisibilityDistance(100);
|
||||||
|
|
||||||
|
Hologram hologram = this.plugin.getHologramsManager().create(textData);
|
||||||
|
hologram.createHologram();
|
||||||
|
hologram.updateShownStateFor(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,350 @@
|
|||||||
|
package de.oliver.fancyholograms.commands;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import de.oliver.fancyholograms.commands.hologram.*;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancyholograms.util.PluginUtils;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancynpcs.api.FancyNpcsPlugin;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.TextDisplay;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public final class HologramCMD extends Command {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final FancyHolograms plugin;
|
||||||
|
|
||||||
|
public HologramCMD(@NotNull final FancyHolograms plugin) {
|
||||||
|
super("hologram", "Main command for the FancyHolograms plugin", "/hologram help", List.of("holograms", "holo", "fholo"));
|
||||||
|
|
||||||
|
setPermission("fancyholograms.admin");
|
||||||
|
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean callModificationEvent(@NotNull final Hologram hologram, @NotNull final CommandSender player, @NotNull final HologramData updatedData, @NotNull final HologramUpdateEvent.HologramModification modification) {
|
||||||
|
final var result = new HologramUpdateEvent(hologram, player, updatedData, modification).callEvent();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
MessageHelper.error(player, "Cancelled hologram modification");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
|
||||||
|
if (!testPermission(sender)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
|
||||||
|
MessageHelper.info(sender, Constants.HELP_TEXT + (!PluginUtils.isFancyNpcsEnabled() ? "" : "\n" + Constants.HELP_TEXT_NPCS));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (args[0].equalsIgnoreCase("list")) {
|
||||||
|
return new ListCMD().run(sender, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
MessageHelper.error(sender, "Wrong usage: /hologram help");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (args[0].equalsIgnoreCase("create")) {
|
||||||
|
return new CreateCMD().run(sender, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0].equalsIgnoreCase("nearby")) {
|
||||||
|
return new NearbyCMD().run(sender, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var hologram = this.plugin.getHologramsManager().getHologram(args[1]).orElse(null);
|
||||||
|
if (hologram == null) {
|
||||||
|
MessageHelper.error(sender, "Could not find hologram: '" + args[1] + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return switch (args[0].toLowerCase(Locale.ROOT)) {
|
||||||
|
case "info" -> new InfoCMD().run(sender, hologram, args);
|
||||||
|
case "remove" -> new RemoveCMD().run(sender, hologram, args);
|
||||||
|
case "teleport" -> new TeleportCMD().run(sender, hologram, args);
|
||||||
|
case "copy" -> new CopyCMD().run(sender, hologram, args);
|
||||||
|
case "edit" -> {
|
||||||
|
if (args.length < 3) {
|
||||||
|
MessageHelper.error(sender, "Wrong usage: /hologram help");
|
||||||
|
yield false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var updated = edit(sender, hologram, args);
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
if (sender instanceof Player p) {
|
||||||
|
hologram.forceUpdate();
|
||||||
|
hologram.refreshHologram(p);
|
||||||
|
}
|
||||||
|
hologram.queueUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
yield updated;
|
||||||
|
}
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) throws IllegalArgumentException {
|
||||||
|
if (args.length == 0) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo {tab:action}
|
||||||
|
if (args.length == 1) {
|
||||||
|
return Stream.of("help", "list", "teleport", "create", "remove", "edit", "copy", "info", "nearby").filter(input -> input.startsWith(args[0].toLowerCase(Locale.ROOT))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo create {tab:type}
|
||||||
|
if (args.length == 2 && args[0].equalsIgnoreCase("create")) {
|
||||||
|
return Arrays.asList("text", "item", "block");
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo [action] {tab:hologram}
|
||||||
|
if (args.length == 2) {
|
||||||
|
final var action = args[0].toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
if (!Set.of("teleport", "remove", "edit", "copy", "info").contains(action)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.plugin.getHologramsManager().getPersistentHolograms().stream().map(hologram -> hologram.getData().getName()).filter(input -> input.toLowerCase().startsWith(args[1].toLowerCase(Locale.ROOT))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var hologram = this.plugin.getHologramsManager().getHologram(args[1]).orElse(null);
|
||||||
|
if (hologram == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
HologramType type = hologram.getData().getType();
|
||||||
|
|
||||||
|
// /holo edit [hologram] {tab:option}
|
||||||
|
if (args.length == 3) {
|
||||||
|
if (!args[0].equalsIgnoreCase("edit")) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var usingNpcs = PluginUtils.isFancyNpcsEnabled();
|
||||||
|
|
||||||
|
List<String> suggestions = new ArrayList<>(Arrays.asList("position", "moveHere", "center", "moveTo", "rotate", "rotatepitch", "billboard", "scale", "translate", "visibilityDistance", "visibility", "shadowRadius", "shadowStrength", "brightness", usingNpcs ? "linkWithNpc" : "", usingNpcs ? "unlinkWithNpc" : ""));
|
||||||
|
suggestions.addAll(type.getCommands());
|
||||||
|
|
||||||
|
return suggestions.stream().filter(input -> input.toLowerCase().startsWith(args[2].toLowerCase(Locale.ROOT))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args[0].equalsIgnoreCase("edit")) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo edit [hologram] [option] {tab:contextual}
|
||||||
|
if (args.length == 4) {
|
||||||
|
final var suggestions = switch (args[2].toLowerCase(Locale.ROOT)) {
|
||||||
|
case "billboard" -> {
|
||||||
|
final var values = new ArrayList<>(List.of(Display.Billboard.values()));
|
||||||
|
|
||||||
|
if (hologram.getData() instanceof DisplayHologramData displayData) {
|
||||||
|
values.remove(displayData.getBillboard());
|
||||||
|
}
|
||||||
|
|
||||||
|
yield values.stream().map(Enum::name);
|
||||||
|
}
|
||||||
|
case "background" -> {
|
||||||
|
TextHologramData textData = (TextHologramData) hologram.getData();
|
||||||
|
final var colors = new ArrayList<>(NamedTextColor.NAMES.keys());
|
||||||
|
|
||||||
|
colors.add("reset");
|
||||||
|
colors.add("default");
|
||||||
|
colors.add("transparent");
|
||||||
|
|
||||||
|
final var current = textData.getBackground();
|
||||||
|
|
||||||
|
if (current == null) {
|
||||||
|
colors.remove("reset");
|
||||||
|
colors.remove("default");
|
||||||
|
} else if (current == Hologram.TRANSPARENT) {
|
||||||
|
colors.remove("transparent");
|
||||||
|
} else {
|
||||||
|
NamedTextColor named = current.getAlpha() == 255 ? NamedTextColor.namedColor(current.asRGB()) : null;
|
||||||
|
colors.add(named != null ? named.toString() : '#' + Integer.toHexString(current.asARGB()));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield colors.stream();
|
||||||
|
}
|
||||||
|
case "textshadow" -> {
|
||||||
|
TextHologramData textData = (TextHologramData) hologram.getData();
|
||||||
|
yield Stream.of(!textData.hasTextShadow()).map(Object::toString);
|
||||||
|
}
|
||||||
|
case "brightness" -> Stream.of("block", "sky");
|
||||||
|
case "textalignment" -> Arrays.stream(TextDisplay.TextAlignment.values()).map(Enum::name);
|
||||||
|
case "setline", "removeline" -> {
|
||||||
|
TextHologramData textData = (TextHologramData) hologram.getData();
|
||||||
|
yield IntStream.range(1, textData.getText().size() + 1).mapToObj(Integer::toString);
|
||||||
|
}
|
||||||
|
case "linkwithnpc" -> {
|
||||||
|
if (!PluginUtils.isFancyNpcsEnabled()) {
|
||||||
|
yield Stream.<String>empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
yield FancyNpcsPlugin.get().getNpcManager().getAllNpcs().stream().map(npc -> npc.getData().getName());
|
||||||
|
}
|
||||||
|
case "block" -> Arrays.stream(Material.values()).filter(Material::isBlock).map(Enum::name);
|
||||||
|
case "seethrough" -> Stream.of("true", "false");
|
||||||
|
case "visibility" -> new VisibilityCMD().tabcompletion(sender, hologram, args).stream();
|
||||||
|
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (suggestions != null) {
|
||||||
|
return suggestions.filter(input -> input.toLowerCase().startsWith(args[3].toLowerCase(Locale.ROOT))).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo edit [hologram] setline [number] {tab:line_text}
|
||||||
|
if (args[2].equalsIgnoreCase("setline")) {
|
||||||
|
TextHologramData textData = (TextHologramData) hologram.getData();
|
||||||
|
|
||||||
|
final var index = Ints.tryParse(args[3]);
|
||||||
|
if (index == null || index < 1 || index > textData.getText().size()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return List.of(textData.getText().get(index - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// /holo edit [hologram] moveto {tab:x} {tab:y} {tab:z}
|
||||||
|
if (args[2].equalsIgnoreCase("moveto")) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var suggestions = new ArrayList<String>();
|
||||||
|
suggestions.add("~");
|
||||||
|
suggestions.add("~~");
|
||||||
|
|
||||||
|
if (args.length == 7) {
|
||||||
|
suggestions.add(String.valueOf(player.getLocation().getYaw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 8) {
|
||||||
|
suggestions.add(String.valueOf(player.getLocation().getPitch()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final var target = player.getTargetBlockExact(10);
|
||||||
|
if (target != null) {
|
||||||
|
final var coordinate = switch (args.length) {
|
||||||
|
case 4 -> target.getX();
|
||||||
|
case 5 -> target.getY();
|
||||||
|
case 6 -> target.getZ();
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
suggestions.add(String.valueOf(coordinate));
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args[2].equalsIgnoreCase("brightness")) {
|
||||||
|
if(args.length == 4) {
|
||||||
|
return List.of("block", "sky");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.length > 5) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return List.of("0", "5", "10", "15");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean edit(@NotNull final CommandSender player, @NotNull final Hologram hologram, @NotNull final String[] args) {
|
||||||
|
final var action = args[2].toLowerCase();
|
||||||
|
|
||||||
|
// actions without a data
|
||||||
|
switch (action) {
|
||||||
|
case "position", "movehere" -> {
|
||||||
|
return new MoveHereCMD().run(player, hologram, args);
|
||||||
|
}
|
||||||
|
case "center" -> {
|
||||||
|
return new CenterCMD().run(player, hologram, args);
|
||||||
|
}
|
||||||
|
case "unlinkwithnpc" -> {
|
||||||
|
return new UnlinkWithNpcCMD().run(player, hologram, args);
|
||||||
|
}
|
||||||
|
case "item" -> {
|
||||||
|
return new ItemCMD().run(player, hologram, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 3) {
|
||||||
|
MessageHelper.error(player, "Wrong usage: /hologram help");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (action) {
|
||||||
|
// display data
|
||||||
|
case "moveto" -> new MoveToCMD().run(player, hologram, args);
|
||||||
|
case "rotate" -> new RotateCMD().run(player, hologram, args);
|
||||||
|
case "rotatepitch" -> new RotatePitchCMD().run(player, hologram, args);
|
||||||
|
case "billboard" -> new BillboardCMD().run(player, hologram, args);
|
||||||
|
case "scale" -> new ScaleCMD().run(player, hologram, args);
|
||||||
|
case "translate" -> new TranslateCommand().run(player, hologram, args);
|
||||||
|
case "updatetextinterval" -> new UpdateTextIntervalCMD().run(player, hologram, args);
|
||||||
|
case "visibilitydistance" -> new VisibilityDistanceCMD().run(player, hologram, args);
|
||||||
|
case "visibility" -> new VisibilityCMD().run(player, hologram, args);
|
||||||
|
case "linkwithnpc" -> new LinkWithNpcCMD().run(player, hologram, args);
|
||||||
|
case "shadowradius" -> new ShadowRadiusCMD().run(player, hologram, args);
|
||||||
|
case "shadowstrength" -> new ShadowStrengthCMD().run(player, hologram, args);
|
||||||
|
case "brightness" -> new BrightnessCMD().run(player, hologram, args);
|
||||||
|
|
||||||
|
// text data
|
||||||
|
case "background" -> new BackgroundCMD().run(player, hologram, args);
|
||||||
|
case "addline" -> new AddLineCMD().run(player, hologram, args);
|
||||||
|
case "setline" -> new SetLineCMD().run(player, hologram, args);
|
||||||
|
case "removeline" -> new RemoveLineCMD().run(player, hologram, args);
|
||||||
|
case "insertbefore" -> new InsertBeforeCMD().run(player, hologram, args);
|
||||||
|
case "insertafter" -> new InsertAfterCMD().run(player, hologram, args);
|
||||||
|
case "textshadow" -> new TextShadowCMD().run(player, hologram, args);
|
||||||
|
case "textalignment" -> new TextAlignmentCMD().run(player, hologram, args);
|
||||||
|
case "seethrough" -> new SeeThroughCMD().run(player, hologram, args);
|
||||||
|
|
||||||
|
// block data
|
||||||
|
case "block" -> new BlockCMD().run(player, hologram, args);
|
||||||
|
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package de.oliver.fancyholograms.commands;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface Subcommand {
|
||||||
|
|
||||||
|
List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args);
|
||||||
|
|
||||||
|
boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AddLineCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.line.add"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to add a line to this hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = "";
|
||||||
|
for (int i = 3; i < args.length; i++) {
|
||||||
|
text += args[i] + " ";
|
||||||
|
}
|
||||||
|
text = text.substring(0, text.length() - 1);
|
||||||
|
|
||||||
|
return SetLineCMD.setLine(player, hologram, Integer.MAX_VALUE, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Color;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class BackgroundCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.background"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to chnage the background of a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final var color = args[3].toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
final Color background;
|
||||||
|
|
||||||
|
if (color.equals("reset") || color.equals("default")) {
|
||||||
|
background = null;
|
||||||
|
} else {
|
||||||
|
if (color.equals("transparent")) {
|
||||||
|
background = Hologram.TRANSPARENT;
|
||||||
|
} else if (color.startsWith("#")) {
|
||||||
|
Color parsed = Color.fromARGB((int) Long.parseLong(color.substring(1), 16));
|
||||||
|
//make background solid color if RGB hex provided
|
||||||
|
if (color.length() == 7) background = parsed.setAlpha(255);
|
||||||
|
else background = parsed;
|
||||||
|
} else {
|
||||||
|
NamedTextColor named = NamedTextColor.NAMES.value(color.replace(' ', '_'));
|
||||||
|
background = named == null ? null : Color.fromARGB(named.value() | 0xC8000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (background == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse background color");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Objects.equals(background, textData.getBackground())) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this background color");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setBackground(background);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.BACKGROUND)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Objects.equals(copied.getBackground(), textData.getBackground())) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this background color");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setBackground(copied.getBackground());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed background color");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.base.Enums;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class BillboardCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.billboard"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to change the billboard of a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var billboard = Enums.getIfPresent(Display.Billboard.class, args[3].toUpperCase(Locale.ROOT)).orNull();
|
||||||
|
|
||||||
|
if (billboard == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse billboard");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (billboard == displayData.getBillboard()) {
|
||||||
|
MessageHelper.warning(player, "This billboard is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = displayData.copy(displayData.getName());
|
||||||
|
copied.setBillboard(billboard);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.BILLBOARD)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copied.getBillboard() == displayData.getBillboard()) {
|
||||||
|
MessageHelper.warning(player, "This billboard is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayData.setBillboard(copied.getBillboard());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed the billboard to " + StringUtils.capitalize(billboard.name().toLowerCase(Locale.ROOT)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.BlockHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BlockCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.block"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to change the block of this hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof BlockHologramData blockData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on item holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Material block = Material.getMaterial(args[3]);
|
||||||
|
if (block == null) {
|
||||||
|
MessageHelper.error(player, "Could not find block type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block == blockData.getBlock()) {
|
||||||
|
MessageHelper.warning(player, "This block is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = blockData.copy(blockData.getName());
|
||||||
|
copied.setBlock(block);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.BILLBOARD)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copied.getBlock() == blockData.getBlock()) {
|
||||||
|
MessageHelper.warning(player, "This block is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockData.setBlock(block);
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Set block to '" + block.name() + "'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.NumberHelper;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BrightnessCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.length < 5) {
|
||||||
|
MessageHelper.error(player, "You must provide a brightness type and value.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var brightnessType = args[3];
|
||||||
|
|
||||||
|
if(!brightnessType.equalsIgnoreCase("block") && !brightnessType.equalsIgnoreCase("sky")) {
|
||||||
|
MessageHelper.error(player, "Invalid brightness type, valid options are BLOCK or SKY");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var parsedNumber = NumberHelper.parseInt(args[4]);
|
||||||
|
|
||||||
|
if(parsedNumber.isEmpty()) {
|
||||||
|
MessageHelper.error(player, "Invalid brightness value.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var brightnessValue = parsedNumber.get();
|
||||||
|
|
||||||
|
if(brightnessValue < 0 || brightnessValue > 15) {
|
||||||
|
MessageHelper.error(player, "Invalid brightness value, must be between 0 and 15");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var currentBrightness = displayData.getBrightness();
|
||||||
|
final var blockBrightness = brightnessType.equalsIgnoreCase("block") ? brightnessValue :
|
||||||
|
currentBrightness == null ? 0 : currentBrightness.getBlockLight();
|
||||||
|
final var skyBrightness = brightnessType.equalsIgnoreCase("sky") ? brightnessValue :
|
||||||
|
currentBrightness == null ? 0 : currentBrightness.getSkyLight();
|
||||||
|
|
||||||
|
displayData.setBrightness(new Display.Brightness(blockBrightness, skyBrightness));
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed " + brightnessType.toLowerCase() + " brightness to " + brightnessValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CenterCMD implements Subcommand {
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.center"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to center a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location location = hologram.getData().getLocation();
|
||||||
|
|
||||||
|
location.set(
|
||||||
|
Math.floor(location.x()) + 0.5,
|
||||||
|
location.y(),
|
||||||
|
Math.floor(location.z()) + 0.5
|
||||||
|
);
|
||||||
|
|
||||||
|
hologram.getData().setLocation(location);
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Centered the hologram to %s/%s/%s %s\u00B0 %s\u00B0".formatted(
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(location.x()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(location.y()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(location.z()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format((location.getYaw() + 180f) % 360f),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format((location.getPitch()) % 360f)
|
||||||
|
));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramCreateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CopyCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.copy"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to clone a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (args.length < 3) {
|
||||||
|
MessageHelper.error(sender, "Wrong usage: /hologram help");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = args[2];
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramsManager().getHologram(name).isPresent()) {
|
||||||
|
MessageHelper.error(sender, "There already exists a hologram with this name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.contains(".")) {
|
||||||
|
MessageHelper.error(sender, "The name of the hologram cannot contain a dot");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var data = hologram.getData().copy(name);
|
||||||
|
Location originalLocation = data.getLocation();
|
||||||
|
Location location = player.getLocation();
|
||||||
|
location.setPitch(originalLocation.getPitch());
|
||||||
|
location.setYaw(originalLocation.getYaw());
|
||||||
|
data.setLocation(location);
|
||||||
|
|
||||||
|
final var copy = FancyHolograms.get().getHologramsManager().create(data);
|
||||||
|
|
||||||
|
if (!new HologramCreateEvent(copy, player).callEvent()) {
|
||||||
|
MessageHelper.error(sender, "Creating the copied hologram was cancelled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.createHologram();
|
||||||
|
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||||
|
copy.updateShownStateFor(onlinePlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramsManager().addHologram(copy);
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(sender, "Copied the hologram");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramCreateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CreateCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.create"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to create a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
MessageHelper.error(player, "Wrong usage: /hologram help");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HologramType type = HologramType.getByName(args[1]);
|
||||||
|
if (type == null) {
|
||||||
|
MessageHelper.error(player, "Could not find type: " + args[1]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = args[2];
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramsManager().getHologram(name).isPresent()) {
|
||||||
|
MessageHelper.error(player, "There already exists a hologram with this name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.contains(".")) {
|
||||||
|
MessageHelper.error(player, "The name of the hologram cannot contain a dot");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayHologramData displayData = null;
|
||||||
|
switch (type) {
|
||||||
|
case TEXT -> displayData = new TextHologramData(name, player.getLocation());
|
||||||
|
case ITEM -> {
|
||||||
|
displayData = new ItemHologramData(name, player.getLocation());
|
||||||
|
displayData.setBillboard(Display.Billboard.FIXED);
|
||||||
|
}
|
||||||
|
case BLOCK -> {
|
||||||
|
displayData = new BlockHologramData(name, player.getLocation());
|
||||||
|
displayData.setBillboard(Display.Billboard.FIXED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final var holo = FancyHolograms.get().getHologramsManager().create(displayData);
|
||||||
|
if (!new HologramCreateEvent(holo, player).callEvent()) {
|
||||||
|
MessageHelper.error(player, "Creating the hologram was cancelled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
holo.createHologram();
|
||||||
|
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||||
|
holo.updateShownStateFor(onlinePlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramsManager().addHologram(holo);
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Created the hologram");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InfoCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.info"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to view the info of a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HologramData data = hologram.getData();
|
||||||
|
|
||||||
|
MessageHelper.info(player, "<b>Information about the " + hologram.getData().getName() + " hologram:");
|
||||||
|
MessageHelper.info(player, "Name: <gray>" + hologram.getData().getName());
|
||||||
|
MessageHelper.info(player, "Type: <gray>" + hologram.getData().getType().name());
|
||||||
|
MessageHelper.info(player, "Location: <gray>" + data.getLocation().getWorld().getName() + " " + data.getLocation().getX() + " / " + data.getLocation().getY() + " / " + data.getLocation().getZ());
|
||||||
|
MessageHelper.info(player, "Visibility distance: <gray>" + data.getVisibilityDistance() + " blocks");
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
Vector3f scale = displayData.getScale();
|
||||||
|
if (scale.x() == scale.y() && scale.y() == scale.z()) {
|
||||||
|
MessageHelper.info(player, "Scale: <gray>x" + displayData.getScale().x());
|
||||||
|
} else {
|
||||||
|
MessageHelper.info(player, "Scale: <gray>" + displayData.getScale().x() + ", " + displayData.getScale().y() + ", " + displayData.getScale().z());
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.info(player, "Billboard: <gray>" + displayData.getBillboard().name());
|
||||||
|
MessageHelper.info(player, "Shadow radius: <gray>" + displayData.getShadowRadius());
|
||||||
|
MessageHelper.info(player, "Shadow strength: <gray>" + displayData.getShadowStrength());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.getLinkedNpcName() != null) {
|
||||||
|
MessageHelper.info(player, "Linked npc: <gray>" + data.getLinkedNpcName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof TextHologramData textData) {
|
||||||
|
MessageHelper.info(player, "Text: ");
|
||||||
|
for (String line : textData.getText()) {
|
||||||
|
MessageHelper.info(player, " <reset> " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textData.getBackground() != null) {
|
||||||
|
MessageHelper.info(player, "Background: <gray>" + '#' + Integer.toHexString(textData.getBackground().asARGB()));
|
||||||
|
} else {
|
||||||
|
MessageHelper.info(player, "Background: <gray>default");
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.info(player, "Text alignment: <gray>" + textData.getTextAlignment().name());
|
||||||
|
MessageHelper.info(player, "See through: <gray>" + (textData.isSeeThrough() ? "enabled" : "disabled"));
|
||||||
|
MessageHelper.info(player, "Text shadow: <gray>" + (textData.hasTextShadow() ? "enabled" : "disabled"));
|
||||||
|
if (textData.getTextUpdateInterval() == -1) {
|
||||||
|
MessageHelper.info(player, "Update text interval: <gray>not updating");
|
||||||
|
} else {
|
||||||
|
MessageHelper.info(player, "Update text interval: <gray>" + textData.getTextUpdateInterval() + " ticks");
|
||||||
|
}
|
||||||
|
} else if (data instanceof BlockHologramData blockData) {
|
||||||
|
MessageHelper.info(player, "Block: <gray>" + blockData.getBlock().name());
|
||||||
|
} else if (data instanceof ItemHologramData itemData) {
|
||||||
|
MessageHelper.info(player, "Item: <gray>" + itemData.getItemStack().getType().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InsertAfterCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.insert_after"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = Ints.tryParse(args[3]);
|
||||||
|
if (index == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse line number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
MessageHelper.error(player, "Invalid line index");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = "";
|
||||||
|
for (int i = 4; i < args.length; i++) {
|
||||||
|
text += args[i] + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
MessageHelper.error(player, "You need to provide a text to insert");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.substring(0, text.length() - 1);
|
||||||
|
|
||||||
|
final var lines = new ArrayList<>(textData.getText());
|
||||||
|
lines.add(Math.min(index, lines.size()), text);
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setText(lines);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TEXT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setText(copied.getText());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Inserted line");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InsertBeforeCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.insert_before"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = Ints.tryParse(args[3]);
|
||||||
|
if (index == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse line number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
index--;
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
MessageHelper.error(player, "Invalid line index");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = "";
|
||||||
|
for (int i = 4; i < args.length; i++) {
|
||||||
|
text += args[i] + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
MessageHelper.error(player, "You need to provide a text to insert");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.substring(0, text.length() - 1);
|
||||||
|
|
||||||
|
final var lines = new ArrayList<>(textData.getText());
|
||||||
|
lines.add(Math.min(index, lines.size()), text);
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setText(lines);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TEXT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setText(copied.getText());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Inserted line");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.ItemHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ItemCMD implements Subcommand {
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.edit.item"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof ItemHologramData itemData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on item holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack item = player.getInventory().getItemInMainHand();
|
||||||
|
if (item.getType() == Material.AIR || item.getAmount() < 1) {
|
||||||
|
MessageHelper.error(player, "You need to hold an item in your hand");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (item == itemData.getItemStack()) {
|
||||||
|
MessageHelper.warning(player, "This item is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = itemData.copy(itemData.getName());
|
||||||
|
copied.setItemStack(item);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.BILLBOARD)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copied.getItemStack() == itemData.getItemStack()) {
|
||||||
|
MessageHelper.warning(player, "This item is already set");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemData.setItemStack(item);
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Set the item to '" + item.getType().name() + "'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.PluginUtils;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancynpcs.api.FancyNpcsPlugin;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LinkWithNpcCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.link"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to link a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PluginUtils.isFancyNpcsEnabled()) {
|
||||||
|
MessageHelper.warning(player, "You need to install the FancyNpcs plugin for this functionality to work");
|
||||||
|
MessageHelper.warning(player, "Download link: <click:open_url:'https://modrinth.com/plugin/fancynpcs/versions'><u>click here</u></click>.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = args[3];
|
||||||
|
|
||||||
|
if (hologram.getData().getLinkedNpcName() != null) {
|
||||||
|
MessageHelper.error(player, "This hologram is already linked with an NPC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var npc = FancyNpcsPlugin.get().getNpcManager().getNpc(name);
|
||||||
|
if (npc == null) {
|
||||||
|
MessageHelper.error(player, "Could not find NPC with that name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hologram.getData().setLinkedNpcName(npc.getData().getName());
|
||||||
|
|
||||||
|
FancyHolograms.get().getHologramsManager().syncHologramWithNpc(hologram);
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Linked hologram with NPC");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ListCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.list"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to list the holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var holograms = FancyHolograms.get().getHologramsManager().getPersistentHolograms();
|
||||||
|
|
||||||
|
if (holograms.isEmpty()) {
|
||||||
|
MessageHelper.warning(player, "There are no holograms. Use '/hologram create' to create one");
|
||||||
|
} else {
|
||||||
|
int page;
|
||||||
|
if (args.length < 2) {
|
||||||
|
page = 1;
|
||||||
|
} else {
|
||||||
|
final var index = Ints.tryParse(args[1]);
|
||||||
|
if (index == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse page number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
page = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pages = holograms.size() / 10 + 1;
|
||||||
|
if (page > pages) {
|
||||||
|
MessageHelper.error(player, "Page %s does not exist".formatted(page));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
MessageHelper.info(player, "<b>List of holograms:</b>");
|
||||||
|
MessageHelper.info(player, "<b>Page %s/%s</b>".formatted(page, pages));
|
||||||
|
holograms.stream()
|
||||||
|
.skip((page - 1) * 10)
|
||||||
|
.limit(10)
|
||||||
|
.forEach(holo -> {
|
||||||
|
final var location = holo.getData().getLocation();
|
||||||
|
if (location == null || location.getWorld() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.info(player,
|
||||||
|
"<hover:show_text:'<gray><i>Click to teleport</i></gray>'><click:run_command:'%s'> - %s (%s/%s/%s in %s)</click></hover>"
|
||||||
|
.formatted("/hologram teleport " + holo.getData().getName(),
|
||||||
|
holo.getData().getName(),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.x()),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.y()),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.z()),
|
||||||
|
location.getWorld().getName()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Doubles;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class MoveHereCMD implements Subcommand {
|
||||||
|
|
||||||
|
public static boolean setLocation(Player player, Hologram hologram, Location location, boolean applyRotation) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.move_here"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to move a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = hologram.getData().copy(hologram.getName());
|
||||||
|
final Location newLocation = (applyRotation)
|
||||||
|
? location
|
||||||
|
: new Location(location.getWorld(), location.x(), location.y(), location.z(), copied.getLocation().getYaw(), copied.getLocation().getPitch());
|
||||||
|
copied.setLocation(newLocation);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.POSITION)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hologram.getData().setLocation(copied.getLocation());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Moved the hologram to %s/%s/%s %s\u00B0 %s\u00B0".formatted(
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(newLocation.x()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(newLocation.y()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format(newLocation.z()),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format((newLocation.getYaw() + 180f) % 360f),
|
||||||
|
Constants.COORDINATES_DECIMAL_FORMAT.format((newLocation.getPitch()) % 360f)
|
||||||
|
));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Double calculateCoordinate(@NotNull final String text, @Nullable final Location originLocation, @NotNull final Location callerLocation, @NotNull final Function<Location, Number> extractor) {
|
||||||
|
final var number = Doubles.tryParse(StringUtils.stripStart(text, "~"));
|
||||||
|
final var target = text.startsWith("~~") ? callerLocation : text.startsWith("~") ? originLocation : null;
|
||||||
|
|
||||||
|
if (number == null) {
|
||||||
|
return target == null ? null : extractor.apply(target).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number + extractor.apply(target).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hologram.getData().getLinkedNpcName() != null) {
|
||||||
|
MessageHelper.error(player, "This hologram is linked with an NPC");
|
||||||
|
MessageHelper.error(player, "To unlink: /hologram edit " + hologram.getData().getName() + " unlinkWithNpc");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var location = player.getLocation();
|
||||||
|
|
||||||
|
return setLocation(player, hologram, location, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MoveToCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.edit.move_to"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to move a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 3) {
|
||||||
|
MessageHelper.error(player, "Wrong usage: /hologram help");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var x = MoveHereCMD.calculateCoordinate(args[3], hologram.getData().getLocation(), player.getLocation(), Location::x);
|
||||||
|
final var y = MoveHereCMD.calculateCoordinate(args[4], hologram.getData().getLocation(), player.getLocation(), Location::y);
|
||||||
|
final var z = MoveHereCMD.calculateCoordinate(args[5], hologram.getData().getLocation(), player.getLocation(), Location::z);
|
||||||
|
|
||||||
|
if (x == null || y == null || z == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse position");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var location = new Location(player.getWorld(), x, y, z, hologram.getData().getLocation().getYaw(), hologram.getData().getLocation().getPitch());
|
||||||
|
|
||||||
|
if (args.length > 6) {
|
||||||
|
final var yaw = MoveHereCMD.calculateCoordinate(args[6], hologram.getData().getLocation(), player.getLocation(), loc -> loc.getYaw() + 180f);
|
||||||
|
|
||||||
|
if (yaw == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse yaw");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
location.setYaw(yaw.floatValue() - 180f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length > 7) {
|
||||||
|
final var pitch = MoveHereCMD.calculateCoordinate(args[7], hologram.getData().getLocation(), player.getLocation(), Location::getPitch);
|
||||||
|
|
||||||
|
if (pitch == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse pitch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
location.setPitch(pitch.floatValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoveHereCMD.setLocation(player, hologram, location, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.Constants;
|
||||||
|
import de.oliver.fancyholograms.util.NumberHelper;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class NearbyCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.nearby"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to see nearby holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(player instanceof Player)) {
|
||||||
|
MessageHelper.error(player, "This is a player only command.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
MessageHelper.error(player, Constants.INVALID_NEARBY_RANGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Integer> range = NumberHelper.parseInt(args[1]);
|
||||||
|
|
||||||
|
if (range.isEmpty()) {
|
||||||
|
MessageHelper.error(player, Constants.INVALID_NEARBY_RANGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location playerLocation = ((Player) player).getLocation().clone();
|
||||||
|
|
||||||
|
List<Map.Entry<Hologram, Double>> nearby = FancyHolograms.get()
|
||||||
|
.getHologramsManager()
|
||||||
|
.getPersistentHolograms()
|
||||||
|
.stream()
|
||||||
|
.filter((holo) -> holo.getData().getLocation().getWorld() == playerLocation.getWorld())
|
||||||
|
.map((holo) -> Map.entry(holo, holo.getData().getLocation().distance(playerLocation)))
|
||||||
|
.filter((entry) -> entry.getValue() <= range.get())
|
||||||
|
.sorted(Comparator.comparingInt(a -> a.getValue().intValue()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (nearby.isEmpty()) {
|
||||||
|
MessageHelper.error(player, "There are no nearby holograms in a radius of %s blocks.".formatted(range.get()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.info(player, "<b>Holograms nearby (%s radius)".formatted(range.get()));
|
||||||
|
nearby.forEach((entry) -> {
|
||||||
|
Hologram holo = entry.getKey();
|
||||||
|
double distance = entry.getValue();
|
||||||
|
|
||||||
|
final var location = holo.getData().getLocation();
|
||||||
|
if (location == null || location.getWorld() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.info(player,
|
||||||
|
"<hover:show_text:'<gray><i>Click to teleport</i></gray>'><click:run_command:'%s'> - %s (%s/%s/%s in %s, %s blocks away)</click></hover>"
|
||||||
|
.formatted(
|
||||||
|
"/hologram teleport " + holo.getData().getName(),
|
||||||
|
holo.getData().getName(),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.x()),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.y()),
|
||||||
|
Constants.DECIMAL_FORMAT.format(location.z()),
|
||||||
|
location.getWorld().getName(),
|
||||||
|
Constants.DECIMAL_FORMAT.format(distance)
|
||||||
|
));
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.FancyHologramsPlugin;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramDeleteEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoveCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.remove"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to remove a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new HologramDeleteEvent(hologram, player).callEvent()) {
|
||||||
|
MessageHelper.error(player, "Removing the hologram was cancelled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHologramsPlugin.get().getHologramThread().submit(() -> {
|
||||||
|
FancyHolograms.get().getHologramsManager().removeHologram(hologram);
|
||||||
|
MessageHelper.success(player, "Removed the hologram");
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoveLineCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.line.remove"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to remove a line from a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var index = Ints.tryParse(args[3]);
|
||||||
|
if (index == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse line number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SetLineCMD.setLine(player, hologram, index - 1, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RotateCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.edit.rotate"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to rotate a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var yaw = MoveHereCMD.calculateCoordinate(args[3], hologram.getData().getLocation(), player.getLocation(), loc -> loc.getYaw() + 180f);
|
||||||
|
Location location = hologram.getData().getLocation().clone();
|
||||||
|
location.setYaw(yaw.floatValue() - 180f);
|
||||||
|
|
||||||
|
return MoveHereCMD.setLocation(player, hologram, location, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RotatePitchCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.edit.rotate_pitch"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to rotate a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var pitch = MoveHereCMD.calculateCoordinate(args[3], hologram.getData().getLocation(), player.getLocation(), loc -> loc.getPitch() - 180f);
|
||||||
|
Location location = hologram.getData().getLocation().clone();
|
||||||
|
location.setPitch(pitch.floatValue());
|
||||||
|
|
||||||
|
return MoveHereCMD.setLocation(player, hologram, location, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Floats;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ScaleCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.scale"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to change the scale of a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var scaleX = Floats.tryParse(args[3]);
|
||||||
|
final var scaleY = args.length >= 6 ? Floats.tryParse(args[4]) : scaleX;
|
||||||
|
final var scaleZ = args.length >= 6 ? Floats.tryParse(args[5]) : scaleX;
|
||||||
|
|
||||||
|
if (scaleX == null || scaleY == null || scaleZ == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse scale");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(scaleX, displayData.getScale().x()) == 0 &&
|
||||||
|
Float.compare(scaleY, displayData.getScale().y()) == 0 &&
|
||||||
|
Float.compare(scaleZ, displayData.getScale().z()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram is already at this scale");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = displayData.copy(displayData.getName());
|
||||||
|
copied.setScale(new Vector3f(scaleX, scaleY, scaleZ));
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.SCALE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(copied.getScale().x(), displayData.getScale().x()) == 0 &&
|
||||||
|
Float.compare(copied.getScale().y(), displayData.getScale().y()) == 0 &&
|
||||||
|
Float.compare(copied.getScale().z(), displayData.getScale().z()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram is already at this scale");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayData.setScale(new Vector3f(
|
||||||
|
copied.getScale().x(),
|
||||||
|
copied.getScale().y(),
|
||||||
|
copied.getScale().z()));
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed scale to " + scaleX + ", " + scaleY + ", " + scaleZ);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class SeeThroughCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.see_trough"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var enabled = switch (args[3].toLowerCase(Locale.ROOT)) {
|
||||||
|
case "true" -> true;
|
||||||
|
case "false" -> false;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enabled == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse see through flag");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled == textData.isSeeThrough()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has see through " + (enabled ? "enabled" : "disabled"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setSeeThrough(enabled);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.SEE_THROUGH)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled == textData.isSeeThrough()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has see through " + (enabled ? "enabled" : "disabled"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setSeeThrough(copied.isSeeThrough());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed see through");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SetLineCMD implements Subcommand {
|
||||||
|
|
||||||
|
public static boolean setLine(CommandSender player, Hologram hologram, int index, String text) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.line.set"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to set a line to this hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var lines = new ArrayList<>(textData.getText());
|
||||||
|
|
||||||
|
if (index >= lines.size()) {
|
||||||
|
lines.add(text == null ? " " : text);
|
||||||
|
} else if (text == null) {
|
||||||
|
lines.remove(index);
|
||||||
|
} else {
|
||||||
|
lines.set(index, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setText(lines);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TEXT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setText(copied.getText());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed text for line " + (Math.min(index, lines.size() - 1) + 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
var index = Ints.tryParse(args[3]);
|
||||||
|
if (index == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse line number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
MessageHelper.error(player, "Invalid line index");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
index--;
|
||||||
|
|
||||||
|
String text = "";
|
||||||
|
for (int i = 4; i < args.length; i++) {
|
||||||
|
text += args[i] + " ";
|
||||||
|
}
|
||||||
|
text = text.substring(0, text.length() - 1);
|
||||||
|
|
||||||
|
return setLine(player, hologram, index, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Floats;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ShadowRadiusCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.shadow_radius"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var radius = Floats.tryParse(args[3]);
|
||||||
|
|
||||||
|
if (radius == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse shadow radius");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(radius, displayData.getShadowRadius()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this shadow radius");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = displayData.copy(displayData.getName());
|
||||||
|
copied.setShadowRadius(radius);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.SHADOW_RADIUS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(copied.getShadowRadius(), displayData.getShadowRadius()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this shadow radius");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayData.setShadowRadius(copied.getShadowRadius());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed shadow radius");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Floats;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ShadowStrengthCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.shadow_strength"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var strength = Floats.tryParse(args[3]);
|
||||||
|
|
||||||
|
if (strength == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse shadow strength");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(strength, displayData.getShadowStrength()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this shadow strength");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = displayData.copy(displayData.getName());
|
||||||
|
copied.setShadowStrength(strength);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.SHADOW_STRENGTH)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(copied.getShadowStrength(), displayData.getShadowStrength()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this shadow strength");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayData.setShadowStrength(copied.getShadowStrength());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed shadow strength");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TeleportCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender sender, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(sender.hasPermission("fancyholograms.hologram.teleport"))) {
|
||||||
|
MessageHelper.error(sender, "You don't have the required permission to teleport you to a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
MessageHelper.error(sender, "You must be a sender to use this command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var location = hologram.getData().getLocation();
|
||||||
|
|
||||||
|
if (location == null || location.getWorld() == null) {
|
||||||
|
MessageHelper.error(player, "Could not teleport to the hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.teleportAsync(location).thenAccept(success -> {
|
||||||
|
if (success) MessageHelper.success(player, "Teleported you to the hologram");
|
||||||
|
else MessageHelper.error(player, "Could not teleport to the hologram");
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.base.Enums;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.TextDisplay;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class TextAlignmentCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.text_alignment"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var alignment = Enums.getIfPresent(TextDisplay.TextAlignment.class, args[3].toUpperCase(Locale.ROOT)).orNull();
|
||||||
|
|
||||||
|
if (alignment == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse text alignment");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textData.getTextAlignment() == alignment) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this text alignment");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setTextAlignment(alignment);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TEXT_ALIGNMENT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textData.getTextAlignment() == alignment) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this text alignment");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setTextAlignment(((TextHologramData) copied).getTextAlignment());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed text alignment");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class TextShadowCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.text_shadow"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var enabled = switch (args[3].toLowerCase(Locale.ROOT)) {
|
||||||
|
case "true" -> true;
|
||||||
|
case "false" -> false;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enabled == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse text shadow flag");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled == textData.hasTextShadow()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has text shadow " + (enabled ? "enabled" : "disabled"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setTextShadow(enabled);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TEXT_SHADOW)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled == textData.hasTextShadow()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has text shadow " + (enabled ? "enabled" : "disabled"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setTextShadow(copied.hasTextShadow());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed text shadow");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Floats;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TranslateCommand implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.translate"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to change the translation of a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var translateX = Floats.tryParse(args[3]);
|
||||||
|
final var translateY = args.length >= 6 ? Floats.tryParse(args[4]) : translateX;
|
||||||
|
final var translateZ = args.length >= 6 ? Floats.tryParse(args[5]) : translateX;
|
||||||
|
|
||||||
|
if (translateX == null || translateY == null || translateZ == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse translation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof DisplayHologramData displayData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on display holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(translateX, displayData.getTranslation().x()) == 0 &&
|
||||||
|
Float.compare(translateY, displayData.getTranslation().y()) == 0 &&
|
||||||
|
Float.compare(translateZ, displayData.getTranslation().z()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram is already at this translation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = displayData.copy(displayData.getName());
|
||||||
|
copied.setTranslation(new Vector3f(translateX, translateY, translateZ));
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.TRANSLATION)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.compare(copied.getTranslation().x(), displayData.getTranslation().x()) == 0 &&
|
||||||
|
Float.compare(copied.getTranslation().y(), displayData.getTranslation().y()) == 0 &&
|
||||||
|
Float.compare(copied.getTranslation().z(), displayData.getTranslation().z()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram is already at this translation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayData.setTranslation(new Vector3f(
|
||||||
|
copied.getTranslation().x(),
|
||||||
|
copied.getTranslation().y(),
|
||||||
|
copied.getTranslation().z()));
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed translation to " + translateX + ", " + translateY + ", " + translateZ);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancyholograms.util.PluginUtils;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancynpcs.api.FancyNpcsPlugin;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UnlinkWithNpcCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.unlink"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to unlink a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PluginUtils.isFancyNpcsEnabled()) {
|
||||||
|
MessageHelper.warning(player, "You need to install the FancyNpcs plugin for this functionality to work");
|
||||||
|
MessageHelper.warning(player, "Download link: <click:open_url:'https://modrinth.com/plugin/fancynpcs/versions'><u>click here</u></click>.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hologram.getData().getLinkedNpcName() == null) {
|
||||||
|
MessageHelper.error(player, "This hologram is not linked with an NPC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var npc = FancyNpcsPlugin.get().getNpcManager().getNpc(hologram.getData().getLinkedNpcName());
|
||||||
|
|
||||||
|
hologram.getData().setLinkedNpcName(null);
|
||||||
|
|
||||||
|
if (npc != null) {
|
||||||
|
npc.getData().setDisplayName(npc.getData().getName());
|
||||||
|
npc.updateForAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Unlinked hologram with NPC");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class UpdateTextIntervalCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.text_interval"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hologram.getData() instanceof TextHologramData textData)) {
|
||||||
|
MessageHelper.error(player, "This command can only be used on text holograms");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var text = args[3].toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
Integer interval;
|
||||||
|
|
||||||
|
if (text.equals("never") || text.equals("off") || text.equals("none")) {
|
||||||
|
interval = -1;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var multiplier = 1;
|
||||||
|
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
switch (text.charAt(text.length() - 1)) {
|
||||||
|
case 's' -> multiplier = 1000;
|
||||||
|
case 'm' -> multiplier = 1000 * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final var time = Ints.tryParse(multiplier == 1 ? text : text.substring(0, text.length() - 1));
|
||||||
|
|
||||||
|
if (time == null) {
|
||||||
|
interval = null;
|
||||||
|
} else {
|
||||||
|
interval = time * multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse text update interval");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval == textData.getTextUpdateInterval()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this text update interval");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = Math.max(-1, interval);
|
||||||
|
|
||||||
|
final var copied = textData.copy(textData.getName());
|
||||||
|
copied.setTextUpdateInterval(interval);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.UPDATE_TEXT_INTERVAL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copied.getTextUpdateInterval() == textData.getTextUpdateInterval()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this text update interval");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textData.setTextUpdateInterval(copied.getTextUpdateInterval());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed the text update interval");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.data.property.Visibility;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class VisibilityCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return Arrays.stream(
|
||||||
|
Visibility.values()
|
||||||
|
).map(Objects::toString).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.visibility"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var optionalVisibility = Visibility.byString(args[3]);
|
||||||
|
if (hologram == null || optionalVisibility.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final var visibility = optionalVisibility.get();
|
||||||
|
|
||||||
|
final var copied = hologram.getData().copy(hologram.getName());
|
||||||
|
copied.setVisibility(visibility);
|
||||||
|
|
||||||
|
if (hologram.getData().getVisibility() == copied.getVisibility()) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has visibility set to " + visibility);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hologram.getData().setVisibility(copied.getVisibility());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed visibility to " + visibility);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package de.oliver.fancyholograms.commands.hologram;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramUpdateEvent;
|
||||||
|
import de.oliver.fancyholograms.commands.HologramCMD;
|
||||||
|
import de.oliver.fancyholograms.commands.Subcommand;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class VisibilityDistanceCMD implements Subcommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabcompletion(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(@NotNull CommandSender player, @Nullable Hologram hologram, @NotNull String[] args) {
|
||||||
|
|
||||||
|
if (!(player.hasPermission("fancyholograms.hologram.edit.visibility_distance"))) {
|
||||||
|
MessageHelper.error(player, "You don't have the required permission to edit a hologram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibilityDistance = Ints.tryParse(args[3]);
|
||||||
|
|
||||||
|
if (visibilityDistance == null) {
|
||||||
|
MessageHelper.error(player, "Could not parse visibility distance");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibilityDistance <= 0) {
|
||||||
|
visibilityDistance = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ints.compare(visibilityDistance, hologram.getData().getVisibilityDistance()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this visibility distance");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var copied = hologram.getData().copy(hologram.getName());
|
||||||
|
copied.setVisibilityDistance(visibilityDistance);
|
||||||
|
|
||||||
|
if (!HologramCMD.callModificationEvent(hologram, player, copied, HologramUpdateEvent.HologramModification.UPDATE_VISIBILITY_DISTANCE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ints.compare(copied.getVisibilityDistance(), hologram.getData().getVisibilityDistance()) == 0) {
|
||||||
|
MessageHelper.warning(player, "This hologram already has this visibility distance");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hologram.getData().setVisibilityDistance(copied.getVisibilityDistance());
|
||||||
|
|
||||||
|
if (FancyHolograms.get().getHologramConfiguration().isSaveOnChangedEnabled()) {
|
||||||
|
FancyHolograms.get().getHologramStorage().save(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHelper.success(player, "Changed visibility distance");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package de.oliver.fancyholograms.hologram.version;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.data.*;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramHideEvent;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancysitula.api.entities.*;
|
||||||
|
import de.oliver.fancysitula.factories.FancySitula;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
|
||||||
|
public final class HologramImpl extends Hologram {
|
||||||
|
|
||||||
|
private FS_Display fsDisplay;
|
||||||
|
|
||||||
|
public HologramImpl(@NotNull final HologramData data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEntityId() {
|
||||||
|
return fsDisplay.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable org.bukkit.entity.Display getDisplayEntity() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create() {
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (!location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.getType()) {
|
||||||
|
case TEXT -> this.fsDisplay = new FS_TextDisplay();
|
||||||
|
case ITEM -> this.fsDisplay = new FS_ItemDisplay();
|
||||||
|
case BLOCK -> this.fsDisplay = new FS_BlockDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData dd) {
|
||||||
|
fsDisplay.setTransformationInterpolationDuration(dd.getInterpolationDuration());
|
||||||
|
fsDisplay.setTransformationInterpolationStartDeltaTicks(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
this.fsDisplay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
if (fsDisplay == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// location data
|
||||||
|
final var location = data.getLocation();
|
||||||
|
if (location.getWorld() == null || !location.isWorldLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fsDisplay.setLocation(location);
|
||||||
|
|
||||||
|
if (fsDisplay instanceof FS_TextDisplay textDisplay && data instanceof TextHologramData textData) {
|
||||||
|
// line width
|
||||||
|
textDisplay.setLineWidth(Hologram.LINE_WIDTH);
|
||||||
|
|
||||||
|
// background
|
||||||
|
final var background = textData.getBackground();
|
||||||
|
if (background == null) {
|
||||||
|
textDisplay.setBackground(1073741824); // default background
|
||||||
|
} else if (background == Hologram.TRANSPARENT) {
|
||||||
|
textDisplay.setBackground(0);
|
||||||
|
} else {
|
||||||
|
textDisplay.setBackground(background.asARGB());
|
||||||
|
}
|
||||||
|
|
||||||
|
textDisplay.setStyleFlags((byte) 0);
|
||||||
|
textDisplay.setShadow(textData.hasTextShadow());
|
||||||
|
textDisplay.setSeeThrough(textData.isSeeThrough());
|
||||||
|
|
||||||
|
switch (textData.getTextAlignment()) {
|
||||||
|
case LEFT -> textDisplay.setAlignLeft(true);
|
||||||
|
case RIGHT -> textDisplay.setAlignRight(true);
|
||||||
|
case CENTER -> {
|
||||||
|
textDisplay.setAlignLeft(false);
|
||||||
|
textDisplay.setAlignRight(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fsDisplay instanceof FS_ItemDisplay itemDisplay && data instanceof ItemHologramData itemData) {
|
||||||
|
// item
|
||||||
|
itemDisplay.setItem(itemData.getItemStack());
|
||||||
|
} else if (fsDisplay instanceof FS_BlockDisplay blockDisplay && data instanceof BlockHologramData blockData) {
|
||||||
|
// block
|
||||||
|
|
||||||
|
// BlockType blockType = RegistryAccess.registryAccess().getRegistry(RegistryKey.BLOCK).get(blockData.getBlock().getKey());
|
||||||
|
blockDisplay.setBlock(blockData.getBlock().createBlockData().createBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data instanceof DisplayHologramData displayData) {
|
||||||
|
// billboard data
|
||||||
|
fsDisplay.setBillboard(FS_Display.Billboard.valueOf(displayData.getBillboard().name()));
|
||||||
|
|
||||||
|
// brightness
|
||||||
|
if (displayData.getBrightness() != null) {
|
||||||
|
fsDisplay.setBrightnessOverride(displayData.getBrightness().getBlockLight() << 4 | displayData.getBrightness().getSkyLight() << 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity transformation
|
||||||
|
fsDisplay.setTranslation(displayData.getTranslation());
|
||||||
|
fsDisplay.setScale(displayData.getScale());
|
||||||
|
fsDisplay.setLeftRotation(new Quaternionf());
|
||||||
|
fsDisplay.setRightRotation(new Quaternionf());
|
||||||
|
|
||||||
|
// entity shadow
|
||||||
|
fsDisplay.setShadowRadius(displayData.getShadowRadius());
|
||||||
|
fsDisplay.setShadowStrength(displayData.getShadowStrength());
|
||||||
|
|
||||||
|
fsDisplay.setViewRange(displayData.getVisibilityDistance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean show(@NotNull final Player player) {
|
||||||
|
if (!new HologramShowEvent(this, player).callEvent()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fsDisplay == null) {
|
||||||
|
create(); // try to create it if it doesn't exist every time
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsDisplay == null) {
|
||||||
|
return false; // could not be created, nothing to show
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cache player protocol version
|
||||||
|
// TODO: fix this
|
||||||
|
// final var protocolVersion = FancyHologramsPlugin.get().isUsingViaVersion() ? Via.getAPI().getPlayerVersion(player) : MINIMUM_PROTOCOL_VERSION;
|
||||||
|
// if (protocolVersion < MINIMUM_PROTOCOL_VERSION) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
FS_RealPlayer fsPlayer = new FS_RealPlayer(player);
|
||||||
|
FancySitula.ENTITY_FACTORY.spawnEntityFor(fsPlayer, fsDisplay);
|
||||||
|
|
||||||
|
this.viewers.add(player.getUniqueId());
|
||||||
|
refreshHologram(player);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hide(@NotNull final Player player) {
|
||||||
|
if (!new HologramHideEvent(this, player).callEvent()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsDisplay == null) {
|
||||||
|
return false; // doesn't exist, nothing to hide
|
||||||
|
}
|
||||||
|
|
||||||
|
FS_RealPlayer fsPlayer = new FS_RealPlayer(player);
|
||||||
|
FancySitula.ENTITY_FACTORY.despawnEntityFor(fsPlayer, fsDisplay);
|
||||||
|
|
||||||
|
this.viewers.remove(player.getUniqueId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh(@NotNull final Player player) {
|
||||||
|
if (fsDisplay == null) {
|
||||||
|
return; // doesn't exist, nothing to refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isViewer(player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FS_RealPlayer fsPlayer = new FS_RealPlayer(player);
|
||||||
|
|
||||||
|
FancySitula.PACKET_FACTORY.createTeleportEntityPacket(
|
||||||
|
fsDisplay.getId(),
|
||||||
|
data.getLocation().x(),
|
||||||
|
data.getLocation().y(),
|
||||||
|
data.getLocation().z(),
|
||||||
|
data.getLocation().getYaw(),
|
||||||
|
data.getLocation().getPitch(),
|
||||||
|
true)
|
||||||
|
.send(fsPlayer);
|
||||||
|
|
||||||
|
|
||||||
|
if (fsDisplay instanceof FS_TextDisplay textDisplay) {
|
||||||
|
textDisplay.setText(getShownText(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
FancySitula.ENTITY_FACTORY.setEntityDataFor(fsPlayer, fsDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package de.oliver.fancyholograms.listeners;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FHFeatureFlags;
|
||||||
|
import de.oliver.fancyholograms.api.events.HologramShowEvent;
|
||||||
|
import de.oliver.fancyholograms.util.PluginUtils;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
|
|
||||||
|
public class BedrockPlayerListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onHologramShow(final HologramShowEvent event) {
|
||||||
|
if (FHFeatureFlags.DISABLE_HOLOGRAMS_FOR_BEDROCK_PLAYERS.isEnabled() && PluginUtils.isFloodgateEnabled()) {
|
||||||
|
boolean isBedrockPlayer = FloodgateApi.getInstance().isFloodgatePlayer(event.getPlayer().getUniqueId());
|
||||||
|
if (isBedrockPlayer) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package de.oliver.fancyholograms.listeners;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancylib.FancyLib;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancynpcs.api.events.NpcModifyEvent;
|
||||||
|
import de.oliver.fancynpcs.api.events.NpcRemoveEvent;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class NpcListener implements Listener {
|
||||||
|
|
||||||
|
private final @NotNull FancyHolograms plugin;
|
||||||
|
|
||||||
|
public NpcListener(@NotNull final FancyHolograms plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onRemove(@NotNull final NpcRemoveEvent event) {
|
||||||
|
this.plugin.getHologramsManager()
|
||||||
|
.getHolograms()
|
||||||
|
.stream()
|
||||||
|
.filter(hologram -> event.getNpc().getData().getName().equals(hologram.getData().getLinkedNpcName()))
|
||||||
|
.forEach(hologram -> hologram.getData().setLinkedNpcName(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onModify(@NotNull final NpcModifyEvent event) {
|
||||||
|
final var holograms = this.plugin.getHologramsManager().getHolograms();
|
||||||
|
|
||||||
|
switch (event.getModification()) {
|
||||||
|
case TYPE, LOCATION, SCALE -> {
|
||||||
|
final var needsToBeUpdated = holograms.stream()
|
||||||
|
.filter(hologram -> event.getNpc().getData().getName().equals(hologram.getData().getLinkedNpcName()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
FancyLib.getInstance().getScheduler().runTaskLater(null, 1L, () -> needsToBeUpdated.forEach(this.plugin.getHologramsManager()::syncHologramWithNpc));
|
||||||
|
}
|
||||||
|
case DISPLAY_NAME, SHOW_IN_TAB -> {
|
||||||
|
final var isLinked = holograms.stream()
|
||||||
|
.map(Hologram::getData)
|
||||||
|
.map(HologramData::getLinkedNpcName)
|
||||||
|
.anyMatch(event.getNpc().getData().getName()::equals);
|
||||||
|
|
||||||
|
if (isLinked) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
MessageHelper.error(event.getModifier(), "This modification is not allowed on a hologram linked npc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package de.oliver.fancyholograms.listeners;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import net.kyori.adventure.resource.ResourcePackStatus;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.*;
|
||||||
|
import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class PlayerListener implements Listener {
|
||||||
|
|
||||||
|
private final @NotNull FancyHolograms plugin;
|
||||||
|
|
||||||
|
private final Map<UUID, List<UUID>> loadingResourcePacks;
|
||||||
|
|
||||||
|
public PlayerListener(@NotNull final FancyHolograms plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.loadingResourcePacks = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onJoin(@NotNull final PlayerJoinEvent event) {
|
||||||
|
for (final var hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
hologram.updateShownStateFor(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.plugin.getHologramConfiguration().areVersionNotificationsMuted() && event.getPlayer().hasPermission("fancyholograms.admin")) {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> FancyHolograms.get().getVersionConfig().checkVersionAndDisplay(event.getPlayer(), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onQuit(@NotNull final PlayerQuitEvent event) {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
for (final var hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
hologram.hideHologram(event.getPlayer());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onTeleport(@NotNull final PlayerTeleportEvent event) {
|
||||||
|
for (final Hologram hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
hologram.updateShownStateFor(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onWorldChange(@NotNull final PlayerChangedWorldEvent event) {
|
||||||
|
for (final Hologram hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
hologram.updateShownStateFor(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onResourcePackStatus(@NotNull final PlayerResourcePackStatusEvent event) {
|
||||||
|
// Skipping event calls before player has fully loaded to the server.
|
||||||
|
// This should fix NPE due to vanillaPlayer.connection being null when sending resource-packs in the configuration stage.
|
||||||
|
if (!event.getPlayer().isOnline())
|
||||||
|
return;
|
||||||
|
final UUID playerUniqueId = event.getPlayer().getUniqueId();
|
||||||
|
final UUID packUniqueId = getResourcePackID(event);
|
||||||
|
// Adding accepted resource-pack to the list of currently loading resource-packs for that player.
|
||||||
|
if (event.getStatus() == Status.ACCEPTED)
|
||||||
|
loadingResourcePacks.computeIfAbsent(playerUniqueId, (___) -> new ArrayList<>()).add(packUniqueId);
|
||||||
|
// Once successfully loaded (or failed to download), removing resource-pack from the map.
|
||||||
|
else if (event.getStatus() == Status.SUCCESSFULLY_LOADED || event.getStatus() == Status.FAILED_DOWNLOAD) {
|
||||||
|
loadingResourcePacks.computeIfAbsent(playerUniqueId, (___) -> new ArrayList<>()).removeIf(uuid -> uuid.equals(packUniqueId));
|
||||||
|
// Refreshing holograms once (possibly) all resource-packs are loaded.
|
||||||
|
if (loadingResourcePacks.get(playerUniqueId) != null && loadingResourcePacks.get(playerUniqueId).isEmpty()) {
|
||||||
|
// Removing player from the map, as they're no longer needed here.
|
||||||
|
loadingResourcePacks.remove(playerUniqueId);
|
||||||
|
// Refreshing holograms as to make sure custom textures are loaded.
|
||||||
|
for (final Hologram hologram : this.plugin.getHologramsManager().getHolograms()) {
|
||||||
|
hologram.refreshHologram(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For 1.20.2 and higher this method returns actual pack identifier, while for older versions, the identifier is a dummy UUID full of zeroes.
|
||||||
|
// Versions prior 1.20.2 supports sending and receiving only one resource-pack and a dummy, constant identifier can be used as a key.
|
||||||
|
private static @NotNull UUID getResourcePackID(final @NotNull PlayerResourcePackStatusEvent event) {
|
||||||
|
try {
|
||||||
|
event.getClass().getMethod("getID");
|
||||||
|
return event.getID();
|
||||||
|
} catch (final @NotNull NoSuchMethodException e) {
|
||||||
|
return new UUID(0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package de.oliver.fancyholograms.listeners;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
|
|
||||||
|
public class WorldListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onWorldLoad(WorldLoadEvent event) {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
FancyHolograms.get().getFancyLogger().info("Loading holograms for world " + event.getWorld().getName());
|
||||||
|
FancyHolograms.get().getHologramsManager().loadHolograms(event.getWorld().getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onWorldUnload(WorldUnloadEvent event) {
|
||||||
|
FancyHolograms.get().getHologramThread().submit(() -> {
|
||||||
|
FancyHolograms.get().getFancyLogger().info("Unloading holograms for world " + event.getWorld().getName());
|
||||||
|
FancyHolograms.get().getHologramsManager().unloadHolograms(event.getWorld().getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package de.oliver.fancyholograms.loaders;
|
||||||
|
|
||||||
|
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
|
||||||
|
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
|
||||||
|
import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class FancyHologramsBootstrapper implements PluginBootstrap {
|
||||||
|
@Override
|
||||||
|
public void bootstrap(@NotNull BootstrapContext bootstrapContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) {
|
||||||
|
return PluginBootstrap.super.createPlugin(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.oliver.fancyholograms.loaders;
|
||||||
|
|
||||||
|
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
|
||||||
|
import io.papermc.paper.plugin.loader.PluginLoader;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class FancyHologramsLoader implements PluginLoader {
|
||||||
|
@Override
|
||||||
|
public void classloader(@NotNull PluginClasspathBuilder pluginClasspathBuilder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package de.oliver.fancyholograms.storage;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.HologramStorage;
|
||||||
|
import de.oliver.fancyholograms.api.data.BlockHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.DisplayHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.ItemHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.Hologram;
|
||||||
|
import de.oliver.fancyholograms.api.hologram.HologramType;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
public class FlatFileHologramStorage implements HologramStorage {
|
||||||
|
|
||||||
|
private static final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
private static final File HOLOGRAMS_CONFIG_FILE = new File("plugins/FancyHolograms/holograms.yml");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveBatch(Collection<Hologram> holograms, boolean override) {
|
||||||
|
lock.readLock().lock();
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
YamlConfiguration config = null;
|
||||||
|
try {
|
||||||
|
config = YamlConfiguration.loadConfiguration(HOLOGRAMS_CONFIG_FILE);
|
||||||
|
|
||||||
|
if (override) {
|
||||||
|
config.set("holograms", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final var hologram : holograms) {
|
||||||
|
writeHologram(config, hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
if (success) {
|
||||||
|
saveConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Saved " + holograms.size() + " holograms to file (override=" + override + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Hologram hologram) {
|
||||||
|
lock.readLock().lock();
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
YamlConfiguration config = null;
|
||||||
|
try {
|
||||||
|
config = YamlConfiguration.loadConfiguration(HOLOGRAMS_CONFIG_FILE);
|
||||||
|
writeHologram(config, hologram);
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
if (success) {
|
||||||
|
saveConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Saved hologram " + hologram.getData().getName() + " to file");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Hologram hologram) {
|
||||||
|
lock.readLock().lock();
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
YamlConfiguration config = null;
|
||||||
|
try {
|
||||||
|
config = YamlConfiguration.loadConfiguration(HOLOGRAMS_CONFIG_FILE);
|
||||||
|
config.set("holograms." + hologram.getData().getName(), null);
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
if (success) {
|
||||||
|
saveConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Deleted hologram " + hologram.getData().getName() + " from file");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Hologram> loadAll() {
|
||||||
|
List<Hologram> holograms = readHolograms(FlatFileHologramStorage.HOLOGRAMS_CONFIG_FILE, null);
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Loaded " + holograms.size() + " holograms from file");
|
||||||
|
return holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Hologram> loadAll(String world) {
|
||||||
|
List<Hologram> holograms = readHolograms(FlatFileHologramStorage.HOLOGRAMS_CONFIG_FILE, world);
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Loaded " + holograms.size() + " holograms from file (world=" + world + ")");
|
||||||
|
return holograms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param world The world to load the holograms from. (null for all worlds)
|
||||||
|
*/
|
||||||
|
private List<Hologram> readHolograms(@NotNull File configFile, @Nullable String world) {
|
||||||
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
|
if (!config.isConfigurationSection("holograms")) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("No holograms section found in config");
|
||||||
|
return new ArrayList<>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int configVersion = config.getInt("version", 1);
|
||||||
|
if (configVersion != 2) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("Config version is not 2, skipping loading holograms");
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("Old config version detected, skipping loading holograms");
|
||||||
|
return new ArrayList<>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Hologram> holograms = new ArrayList<>();
|
||||||
|
|
||||||
|
ConfigurationSection hologramsSection = config.getConfigurationSection("holograms");
|
||||||
|
for (String name : hologramsSection.getKeys(false)) {
|
||||||
|
ConfigurationSection holoSection = hologramsSection.getConfigurationSection(name);
|
||||||
|
if (holoSection == null) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("Could not load hologram section in config");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world != null && !holoSection.getString("location.world").equals(world)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String typeName = holoSection.getString("type");
|
||||||
|
if (typeName == null) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("HologramType was not saved");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
HologramType type = HologramType.getByName(typeName);
|
||||||
|
if (type == null) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("Could not parse HologramType");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayHologramData displayData = null;
|
||||||
|
switch (type) {
|
||||||
|
case TEXT -> displayData = new TextHologramData(name, new Location(null, 0, 0, 0));
|
||||||
|
case ITEM -> displayData = new ItemHologramData(name, new Location(null, 0, 0, 0));
|
||||||
|
case BLOCK -> displayData = new BlockHologramData(name, new Location(null, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!displayData.read(holoSection, name)) {
|
||||||
|
FancyHolograms.get().getFancyLogger().warn("Could not read hologram data - skipping hologram");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hologram hologram = FancyHolograms.get().getHologramManager().create(displayData);
|
||||||
|
holograms.add(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Loaded " + holograms.size() + " holograms from file");
|
||||||
|
return holograms;
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHologram(YamlConfiguration config, Hologram hologram) {
|
||||||
|
@NotNull ConfigurationSection section;
|
||||||
|
if (!config.isConfigurationSection("holograms")) {
|
||||||
|
section = config.createSection("holograms");
|
||||||
|
} else {
|
||||||
|
section = Objects.requireNonNull(config.getConfigurationSection("holograms"));
|
||||||
|
}
|
||||||
|
|
||||||
|
String holoName = hologram.getData().getName();
|
||||||
|
|
||||||
|
ConfigurationSection holoSection = section.getConfigurationSection(holoName);
|
||||||
|
if (holoSection == null) {
|
||||||
|
holoSection = section.createSection(holoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
hologram.getData().write(holoSection, holoName);
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Wrote hologram " + holoName + " to config");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveConfig(YamlConfiguration config) {
|
||||||
|
config.set("version", 2);
|
||||||
|
config.setInlineComments("version", List.of("DO NOT CHANGE"));
|
||||||
|
|
||||||
|
FancyHolograms.get().getFileStorageExecutor().execute(() -> {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
config.save(HOLOGRAMS_CONFIG_FILE);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!FancyHolograms.canGet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyHolograms.get().getFancyLogger().debug("Saved config to file");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package de.oliver.fancyholograms.storage.converter;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class ConverterTarget {
|
||||||
|
private final @NotNull Pattern hologramIdRegex;
|
||||||
|
|
||||||
|
public ConverterTarget(@NotNull Pattern matching) {
|
||||||
|
this.hologramIdRegex = matching;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Pattern getRegex() {
|
||||||
|
return hologramIdRegex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(@NotNull String hologramId) {
|
||||||
|
return hologramIdRegex.asMatchPredicate().test(hologramId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ConverterTarget ALL = new ConverterTarget(Pattern.compile(".*"));
|
||||||
|
public static @NotNull ConverterTarget all() {
|
||||||
|
return ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ConverterTarget ofAll(@NotNull String first, @NotNull String... others) {
|
||||||
|
StringBuilder builder = new StringBuilder(first);
|
||||||
|
|
||||||
|
if (others.length > 0) {
|
||||||
|
builder.append("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(String.join("|", others));
|
||||||
|
|
||||||
|
return new ConverterTarget(Pattern.compile(builder.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ConverterTarget ofSingle(@NotNull String match) {
|
||||||
|
return new ConverterTarget(Pattern.compile(match));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable ConverterTarget ofStringNullable(@NotNull String match) {
|
||||||
|
|
||||||
|
if (match.equalsIgnoreCase("*")) {
|
||||||
|
return all();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new ConverterTarget(Pattern.compile(match));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
package de.oliver.fancyholograms.storage.converter;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.ItemHologramData;
|
||||||
|
import de.oliver.fancyholograms.api.data.TextHologramData;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.entity.Display;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class DecentHologramsConverter extends HologramConverter {
|
||||||
|
private static final float VANILLA_PIXEL_BLOCK_SIZE = 0.0625f;
|
||||||
|
private static final float TEXT_DISPLAY_PIXEL = VANILLA_PIXEL_BLOCK_SIZE / 3;
|
||||||
|
private static final float TEXT_DISPLAY_LINE_HEIGHT = TEXT_DISPLAY_PIXEL * 14;
|
||||||
|
private static final String PROCESS_ICONS_FLAG = "--processIcons";
|
||||||
|
private static final String ICON_PREFIX = "#ICON: ";
|
||||||
|
private static final File DECENT_HOLOGRAMS_DATA = new File("./plugins/DecentHolograms/holograms/");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String getId() {
|
||||||
|
return "DecentHolograms";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRunConverter() {
|
||||||
|
return DECENT_HOLOGRAMS_DATA.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<HologramData> convertHolograms(@NotNull HologramConversionSession spec) {
|
||||||
|
boolean processIcons = Arrays.stream(spec.getAdditionalArguments()).anyMatch((arg) -> arg.equalsIgnoreCase(PROCESS_ICONS_FLAG));
|
||||||
|
|
||||||
|
if (processIcons) {
|
||||||
|
MessageHelper.warning(
|
||||||
|
spec.getInvoker(),
|
||||||
|
"--processIcons argument is experimental and may produce unexpected results."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
MessageHelper.info(
|
||||||
|
spec.getInvoker(),
|
||||||
|
"Any lines containing an #ICON will be removed. You may run with --processIcons to attempt conversion, but this is experimental."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> targetHolograms = getConvertableHolograms()
|
||||||
|
.stream()
|
||||||
|
.filter((id) -> spec.getTarget().matches(id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (targetHolograms.isEmpty()) {
|
||||||
|
throw new RuntimeException("The provided target matches no holograms.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<HologramData> converted = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final String id : targetHolograms) {
|
||||||
|
final List<HologramData> results = convert(id, processIcons);
|
||||||
|
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
spec.logUnsuccessfulConversion(id, "Unable to convert this hologram, there is no convertable content.");
|
||||||
|
} else {
|
||||||
|
spec.logSuccessfulConversion(id, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
converted.addAll(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> getConvertableHolograms() {
|
||||||
|
final File[] files = DECENT_HOLOGRAMS_DATA.listFiles();
|
||||||
|
|
||||||
|
if (files == null || files.length == 0) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.stream(files)
|
||||||
|
.map((file) -> file.getName().replace(".yml", ""))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull List<HologramData> convert(@NotNull String hologramId, boolean processIcons) {
|
||||||
|
final File file = DECENT_HOLOGRAMS_DATA.toPath()
|
||||||
|
.resolve(hologramId.endsWith(".yml") ? hologramId : hologramId + ".yml")
|
||||||
|
.toFile();
|
||||||
|
|
||||||
|
if (!file.exists() || !file.canRead()) {
|
||||||
|
throw new RuntimeException("File does not exist or is not readable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileConfiguration data = YamlConfiguration.loadConfiguration(file);
|
||||||
|
Objects.requireNonNull(data, "No data could be read from the DecentHolograms file!");
|
||||||
|
|
||||||
|
final Location location = parseLocation(data.getString("location"));
|
||||||
|
final double displayRange = data.getDouble("display-range");
|
||||||
|
final int updateInterval = data.getInt("update-interval");
|
||||||
|
|
||||||
|
// TODO handle exceptions here
|
||||||
|
final Object firstPage = data.getMapList("pages")
|
||||||
|
.stream()
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new RuntimeException(String.format("There are no pages for %s!", hologramId)))
|
||||||
|
.get("lines");
|
||||||
|
|
||||||
|
Objects.requireNonNull(firstPage, String.format("There is no first page for %s!", hologramId));
|
||||||
|
|
||||||
|
final List<Map<String, ?>> firstPageSections;
|
||||||
|
|
||||||
|
try {
|
||||||
|
firstPageSections = (List<Map<String, ?>>) firstPage;
|
||||||
|
} catch (ClassCastException ignored) {
|
||||||
|
throw new RuntimeException(String.format("The first page for %s is invalid!", hologramId));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> lines = firstPageSections
|
||||||
|
.stream()
|
||||||
|
.map((line) -> (String) line.get("content"))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!processIcons) {
|
||||||
|
lines = lines.stream()
|
||||||
|
.map((line) -> line.startsWith(ICON_PREFIX) ? "" : line)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextHologramData hologram = new TextHologramData(hologramId, location);
|
||||||
|
|
||||||
|
hologram.setText(lines);
|
||||||
|
hologram.setTextShadow(true);
|
||||||
|
hologram.setTextUpdateInterval(updateInterval);
|
||||||
|
hologram.setVisibilityDistance((int) displayRange);
|
||||||
|
hologram.setBillboard(Display.Billboard.VERTICAL);
|
||||||
|
hologram.setPersistent(true);
|
||||||
|
|
||||||
|
List<HologramData> results = new ArrayList<>();
|
||||||
|
if (processIcons) {
|
||||||
|
results.addAll(convertSplitLines(hologram, firstPageSections));
|
||||||
|
} else {
|
||||||
|
results.add(hologram);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to convert #ICON prefixed lines into item displays.
|
||||||
|
* <p>
|
||||||
|
* This is done off some arbitrary values I found when testing
|
||||||
|
* on another project, and might not be 100% accurate. However,
|
||||||
|
* it should be enough to give users an idea of what it would look
|
||||||
|
* like.
|
||||||
|
*
|
||||||
|
* @author MattMX
|
||||||
|
* @param base The root hologram (background)
|
||||||
|
* @param lines lines from the DecentHolograms hologram's first page.
|
||||||
|
* @return A list of created [HologramData] children.
|
||||||
|
*/
|
||||||
|
private @NotNull List<HologramData> convertSplitLines(@NotNull TextHologramData base, @NotNull List<Map<String, ?>> lines) {
|
||||||
|
final List<HologramData> stack = new ArrayList<>();
|
||||||
|
final List<String> finalBaseLines = new ArrayList<>();
|
||||||
|
stack.add(base);
|
||||||
|
|
||||||
|
int subTypes = 0;
|
||||||
|
float currentYOffset = 0f;
|
||||||
|
|
||||||
|
// Track total height of hologram apx (hologram y is inverted)
|
||||||
|
float totalHeight = 0f;
|
||||||
|
|
||||||
|
for (final Map<String, ?> entry : lines) {
|
||||||
|
final Object contentEntry = entry.get("content");
|
||||||
|
// TODO add height
|
||||||
|
|
||||||
|
if (!(contentEntry instanceof String line)) continue;
|
||||||
|
|
||||||
|
if (line.startsWith(ICON_PREFIX)) {
|
||||||
|
final String materialTypeString = line.replace(ICON_PREFIX, "");
|
||||||
|
final Material material = Material.valueOf(materialTypeString);
|
||||||
|
|
||||||
|
final String formattedId = String.format("%s_icon_%s", base.getName(), subTypes++);
|
||||||
|
final ItemHologramData data = new ItemHologramData(formattedId, base.getLocation());
|
||||||
|
|
||||||
|
data.setItemStack(new ItemStack(material));
|
||||||
|
data.setBillboard(Display.Billboard.VERTICAL);
|
||||||
|
data.setScale(new Vector3f(0.45f, 0.45f, 0.45f));
|
||||||
|
data.setTranslation(new Vector3f(0f, currentYOffset, 0f));
|
||||||
|
data.setVisibilityDistance(base.getVisibilityDistance());
|
||||||
|
data.setPersistent(true);
|
||||||
|
|
||||||
|
float h = TEXT_DISPLAY_LINE_HEIGHT + 0.12f;
|
||||||
|
|
||||||
|
currentYOffset += h;
|
||||||
|
totalHeight += h;
|
||||||
|
|
||||||
|
// Empty space for text
|
||||||
|
// TODO find average item height for now 0.12f seems ok when scale is 0.45f
|
||||||
|
finalBaseLines.addAll(List.of("&r", "&r"));
|
||||||
|
stack.add(data);
|
||||||
|
} else {
|
||||||
|
// Empty line
|
||||||
|
finalBaseLines.add(line);
|
||||||
|
float h = TEXT_DISPLAY_LINE_HEIGHT;
|
||||||
|
|
||||||
|
currentYOffset += h;
|
||||||
|
totalHeight += h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.setText(finalBaseLines);
|
||||||
|
|
||||||
|
// Now invert their y offset
|
||||||
|
for (@NotNull HologramData holo : stack) {
|
||||||
|
if (holo instanceof ItemHologramData itemHolo) {
|
||||||
|
itemHolo.setTranslation(
|
||||||
|
new Vector3f(
|
||||||
|
itemHolo.getTranslation().x,
|
||||||
|
totalHeight - itemHolo.getTranslation().y - 0.25f,
|
||||||
|
itemHolo.getTranslation().z
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull Location parseLocation(@Nullable String location) {
|
||||||
|
Objects.requireNonNull(location, "Location cannot be empty!");
|
||||||
|
|
||||||
|
final String[] split = location.split(":");
|
||||||
|
|
||||||
|
if (split.length != 4) {
|
||||||
|
throw new IllegalStateException(String.format("Location in %s didn't have 4 arguments (split by :)", location));
|
||||||
|
}
|
||||||
|
|
||||||
|
World world = Objects.requireNonNull(Bukkit.getWorld(split[0]), String.format("World does not exist for location %s", location));
|
||||||
|
|
||||||
|
double x = Double.parseDouble(split[1]);
|
||||||
|
double y = Double.parseDouble(split[2]);
|
||||||
|
double z = Double.parseDouble(split[3]);
|
||||||
|
|
||||||
|
return new Location(world, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package de.oliver.fancyholograms.storage.converter;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class FHConversionRegistry {
|
||||||
|
private static final Map<String, HologramConverter> converters = new HashMap<>();
|
||||||
|
|
||||||
|
public static void registerBuiltInConverters() {
|
||||||
|
register(new DecentHologramsConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean register(HologramConverter converter) {
|
||||||
|
return converters.putIfAbsent(converter.getId(), converter) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Optional<HologramConverter> getConverterById(@NotNull String id) {
|
||||||
|
return Optional.ofNullable(converters.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends HologramConverter> @NotNull Optional<T> getConverter(@NotNull String id) {
|
||||||
|
return getConverterById(id)
|
||||||
|
.map((converter) -> {
|
||||||
|
try {
|
||||||
|
return (T) converter;
|
||||||
|
} catch (ClassCastException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends HologramConverter> @NotNull Optional<T> getConverter(@NotNull Class<T> clazz) {
|
||||||
|
return converters.values()
|
||||||
|
.stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.findFirst()
|
||||||
|
.map((converter) -> {
|
||||||
|
try {
|
||||||
|
return (T) converter;
|
||||||
|
} catch (ClassCastException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Set<String> getAllConverterIds() {
|
||||||
|
return converters.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Set<String> getAllUsableConverterIds() {
|
||||||
|
return converters
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter((entry) -> entry.getValue().canRunConverter())
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package de.oliver.fancyholograms.storage.converter;
|
||||||
|
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
import de.oliver.fancylib.translations.message.Message;
|
||||||
|
import net.kyori.adventure.audience.Audience;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HologramConversionSession {
|
||||||
|
private final @NotNull ConverterTarget target;
|
||||||
|
private final @NotNull CommandSender invoker;
|
||||||
|
private final String[] arguments;
|
||||||
|
|
||||||
|
public HologramConversionSession(
|
||||||
|
@NotNull ConverterTarget target
|
||||||
|
) {
|
||||||
|
this(target, Bukkit.getConsoleSender(), new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HologramConversionSession(
|
||||||
|
@NotNull ConverterTarget target,
|
||||||
|
@NotNull CommandSender invoker,
|
||||||
|
@NotNull String[] arguments
|
||||||
|
) {
|
||||||
|
this.target = target;
|
||||||
|
this.invoker = invoker;
|
||||||
|
this.arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConverterTarget getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull CommandSender getInvoker() {
|
||||||
|
return this.invoker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String[] getAdditionalArguments() {
|
||||||
|
return this.arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logUnsuccessfulConversion(@NotNull String oldHologram, @Nullable String message) {
|
||||||
|
if (message != null) {
|
||||||
|
MessageHelper.error(
|
||||||
|
getInvoker(),
|
||||||
|
String.format("There was an issue converting %s: %s", oldHologram, message)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
MessageHelper.error(
|
||||||
|
getInvoker(),
|
||||||
|
String.format("There was an issue converting %s!", oldHologram)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logSuccessfulConversion(@NotNull String oldHologram, @NotNull HologramData result) {
|
||||||
|
logSuccessfulConversion(oldHologram, List.of(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logSuccessfulConversion(@NotNull String oldHologram, @NotNull List<HologramData> results) {
|
||||||
|
MessageHelper.info(
|
||||||
|
getInvoker(),
|
||||||
|
String.format("Successfully converted %s to %s hologram(s).", oldHologram, results.size())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (@NotNull HologramData data : results) {
|
||||||
|
MessageHelper.info(
|
||||||
|
getInvoker(),
|
||||||
|
String.format(" - %s type: %s", data.getName(), data.getType().name())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package de.oliver.fancyholograms.storage.converter;
|
||||||
|
|
||||||
|
import de.oliver.fancyanalytics.api.events.Event;
|
||||||
|
import de.oliver.fancyholograms.FancyHolograms;
|
||||||
|
import de.oliver.fancyholograms.api.data.HologramData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class HologramConverter {
|
||||||
|
|
||||||
|
public abstract @NotNull String getId();
|
||||||
|
|
||||||
|
public abstract boolean canRunConverter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of converted holograms
|
||||||
|
* @param spec Configuration of the hologram conversion
|
||||||
|
* @return A list of converted holograms.
|
||||||
|
*/
|
||||||
|
protected abstract @NotNull List<HologramData> convertHolograms(@NotNull HologramConversionSession spec);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of converted holograms
|
||||||
|
* @param spec Configuration of the hologram conversion
|
||||||
|
* @return A list of converted holograms.
|
||||||
|
*/
|
||||||
|
public final @NotNull List<HologramData> convert(@NotNull HologramConversionSession spec) {
|
||||||
|
List<HologramData> converted = convertHolograms(spec);
|
||||||
|
|
||||||
|
Event event = new Event("HologramsConverted")
|
||||||
|
.withProperty("converter", getId())
|
||||||
|
.withProperty("target", spec.getTarget().getRegex().pattern())
|
||||||
|
.withProperty("amount", String.valueOf(converted.size()));
|
||||||
|
FancyHolograms.get().getFancyAnalytics().sendEvent(event);
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull List<String> getConvertableHolograms() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package de.oliver.fancyholograms.util;
|
||||||
|
|
||||||
|
import de.oliver.fancylib.MessageHelper;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
public enum Constants {
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#########.##");
|
||||||
|
|
||||||
|
public final static DecimalFormat COORDINATES_DECIMAL_FORMAT = new DecimalFormat("#########.##");
|
||||||
|
|
||||||
|
public static final String FH_COMMAND_USAGE = "/fancyholograms <save|reload|version|convert>";
|
||||||
|
|
||||||
|
public static final String HELP_TEXT = """
|
||||||
|
<%primary_color%><b>FancyHolograms commands help:<reset>
|
||||||
|
<%primary_color%>- /hologram help <dark_gray>- <white>Shows all (sub)commands
|
||||||
|
<%primary_color%>- /hologram list <dark_gray>- <white>Shows you a overview of all holograms
|
||||||
|
<%primary_color%>- /hologram nearby <range> <dark_gray>- <white>Shows all holograms nearby you in a range
|
||||||
|
<%primary_color%>- /hologram teleport <name> <dark_gray>- <white>Teleports you to a hologram
|
||||||
|
<%primary_color%>- /hologram create <name> <dark_gray>- <white>Creates a new hologram
|
||||||
|
<%primary_color%>- /hologram remove <name> <dark_gray>- <white>Removes a hologram
|
||||||
|
<%primary_color%>- /hologram copy <hologram> <new name> <dark_gray>- <white>Copies a hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> addLine <text ...> <dark_gray>- <white>Adds a line at the bottom
|
||||||
|
<%primary_color%>- /hologram edit <hologram> removeLine <dark_gray>- <white>Removes a line at the bottom
|
||||||
|
<%primary_color%>- /hologram edit <hologram> insertBefore <line number> <text ...> <dark_gray>- <white>Inserts a line before another
|
||||||
|
<%primary_color%>- /hologram edit <hologram> insertAfter <line number> <text ...> <dark_gray>- <white>Inserts a line after another
|
||||||
|
<%primary_color%>- /hologram edit <hologram> setLine <line number> <text ...> <dark_gray>- <white>Edits the line
|
||||||
|
<%primary_color%>- /hologram edit <hologram> position <dark_gray>- <white>Teleports the hologram to you
|
||||||
|
<%primary_color%>- /hologram edit <hologram> moveTo <x> <y> <z> [yaw] [pitch] <dark_gray>- <white>Teleports the hologram to the coordinates
|
||||||
|
<%primary_color%>- /hologram edit <hologram> rotate <degrees> <dark_gray>- <white>Rotates the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> scale <factor> <dark_gray>- <white>Changes the scale of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> billboard <center|fixed|horizontal|vertical> <factor> <dark_gray>- <white>Changes the billboard of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> background <color> <dark_gray>- <white>Changes the background of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> textShadow <true|false> <dark_gray>- <white>Enables/disables the text shadow
|
||||||
|
<%primary_color%>- /hologram edit <hologram> textAlignment <alignment> <dark_gray>- <white>Sets the text alignment
|
||||||
|
<%primary_color%>- /hologram edit <hologram> seeThrough <true|false> <dark_gray>- <white>Enables/disables whether the text can be seen through blocks
|
||||||
|
<%primary_color%>- /hologram edit <hologram> shadowRadius <value> <dark_gray>- <white>Changes the shadow radius of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> shadowStrength <value> <dark_gray>- <white>Changes the shadow strength of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> brightness <block|sky> <0-15> <dark_gray>- <white>Changes the brightness of the hologram
|
||||||
|
<%primary_color%>- /hologram edit <hologram> updateTextInterval <seconds> <dark_gray>- <white>Sets the interval for updating the text
|
||||||
|
""".replace("%primary_color%", MessageHelper.getPrimaryColor());
|
||||||
|
|
||||||
|
public static final String HELP_TEXT_NPCS = """
|
||||||
|
<%primary_color%>- /hologram edit <hologram> linkWithNpc <npc name> <dark_gray>- <white>Links the hologram with an NPC
|
||||||
|
<%primary_color%>- /hologram edit <hologram> unlinkWithNpc <dark_gray>- <white>Unlinks the hologram with an NPC
|
||||||
|
""".replace("%primary_color%", MessageHelper.getPrimaryColor());
|
||||||
|
|
||||||
|
public static final String INVALID_NEARBY_RANGE = "Provide an integer radius to search for holograms nearby.";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package de.oliver.fancyholograms.util;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class NumberHelper {
|
||||||
|
|
||||||
|
public static Optional<Integer> parseInt(String toParse) {
|
||||||
|
try {
|
||||||
|
return Optional.of(Integer.parseInt(toParse));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user