Celeste-OG Initial Release

This commit is contained in:
NotAlexNoyle committed 2024-07-03 14:19:30 -04:00
1 parent a7e0bb9b5d
commit 2214c80c2a
34 files changed
+1056 -1538

No files matched your search

+36 -20
View File
@@ -1,21 +1,37 @@
# IntelliJ project files
.idea
*.iml
out
gen
*.lnk
target/
.classpath
.project
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# macOS system files
.DS_Store
# gradle build output files
build/
.gradle/
# eclipse project files
.settings/
.gitignore
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
.project
.classpath
bin/
+8 -7
View File
@@ -1,11 +1,12 @@
# Celeste - Add shooting stars to your Minecraft server
Celeste is a simple plugin that adds shooting stars to your Minecraft server. Inspired by the shooting stars from Animal Crossing and the falling stars from Breath of the Wild, this plugin brings some celestial ambience to your Minecraft world.
# Celeste-OG - Add shooting stars to your Minecraft server
Celeste-OG is a 1.19 fork of Celeste maintained for [TrueOG Network](https://true-og.net). Celeste plugin that adds shooting stars to your Minecraft server. Inspired by the shooting stars from Animal Crossing and the falling stars from Breath of the Wild, this plugin brings some celestial ambience to your Minecraft world.
[Check out the overview video](https://www.youtube.com/watch?v=TXdrECjVTiU)
[Download the server plugin on Spigot](https://www.spigotmc.org/resources/celeste.81862/)
[Learn more about Celeste (the plugin, not the owl)](https://idreesinc.com/about-celeste.html?utm_source=github&utm_medium=readme&utm_campaign=celeste)
## Changes over Celeste
- Maven -> Gradle.
- Removed bStats Metrics.
- Removed update checker.
- Resolved 1 deprecation warning in CommandCeleste.java.
- Built with Purpur API 1.19.4.
## Features
- Add shooting stars to the night sky, with configurable frequency
+84
View File
@@ -0,0 +1,84 @@
plugins {
java // Tell gradle this is a java project.
id("io.github.goooler.shadow") version "8.1.8"
eclipse // Import eclipse plugin for IDE integration.
kotlin("jvm") version "1.9.23" // Import kotlin jvm plugin for kotlin/java integration.
}
java {
// Declare java version.
sourceCompatibility = JavaVersion.VERSION_17
}
group = "net.trueog.celeste-og" // Declare bundle identifier.
version = "1.0" // Declare plugin version (will be in .jar).
val apiVersion = "1.19" // Declare minecraft server target version.
tasks.named<ProcessResources>("processResources") {
val props = mapOf(
"version" to version,
"apiVersion" to apiVersion
)
inputs.properties(props) // Indicates to rerun if version changes.
filesMatching("plugin.yml") {
expand(props)
}
}
repositories {
mavenCentral()
maven {
url = uri("https://repo.purpurmc.org/snapshots")
}
}
dependencies {
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") // Declare purpur API version to be packaged.
compileOnly("io.github.miniplaceholders:miniplaceholders-api:2.2.3") // Import MiniPlaceholders API.
}
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
tasks.shadowJar {
exclude("io.github.miniplaceholders.*") // Exclude the MiniPlaceholders package from being shadowed.
minimize()
}
tasks.jar {
dependsOn(tasks.shadowJar)
archiveClassifier.set("part")
}
tasks.shadowJar {
archiveClassifier.set("") // Use empty string instead of null
from("LICENSE") {
into("/")
}
}
tasks.jar {
dependsOn("shadowJar")
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-parameters")
options.encoding = "UTF-8"
options.isFork = true
}
kotlin {
jvmToolchain(17)
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.GRAAL_VM
}
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+249
View File
@@ -0,0 +1,249 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+92
View File
@@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-45
View File
@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.idreesinc</groupId>
<artifactId>Celeste</artifactId>
<version>2.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<outputDirectory>dist</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+1
View File
@@ -0,0 +1 @@
rootProject.name = "Celeste-OG"
@@ -1,60 +0,0 @@
package com.idreesinc.celeste;
import com.idreesinc.celeste.config.CelesteConfig;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.List;
import java.util.Random;
public class Astronomer extends BukkitRunnable {
private final Celeste celeste;
public Astronomer(Celeste celeste) {
this.celeste = celeste;
}
public void run() {
if (celeste.getServer().getOnlinePlayers().size() == 0) {
return;
}
List<World> worlds = celeste.getServer().getWorlds();
for (World world : worlds) {
CelesteConfig config = celeste.configManager.getConfigForWorld(world.getName());
if (!celeste.configManager.doesWorldHaveOverrides(world.getName())
&& !world.getEnvironment().equals(World.Environment.NORMAL)) {
// Ensure that Celeste only runs on normal worlds unless override is specified in config
continue;
}
if (world.getPlayers().size() == 0) {
continue;
}
if (!(world.getTime() >= config.beginSpawningStarsTime && world.getTime() <= config.endSpawningStarsTime)) {
continue;
}
if (world.hasStorm()) {
continue;
}
double shootingStarChance;
double fallingStarChance;
if (config.newMoonMeteorShower && (world.getFullTime() / 24000) % 8 == 4) {
shootingStarChance = config.shootingStarsPerMinuteMeteorShower / 120d;
fallingStarChance = config.fallingStarsPerMinuteMeteorShower / 120d;
} else {
shootingStarChance = config.shootingStarsPerMinute / 120d;
fallingStarChance = config.fallingStarsPerMinute / 120d;
}
if (config.shootingStarsEnabled && new Random().nextDouble() <= shootingStarChance) {
CelestialSphere.createShootingStar(celeste,
world.getPlayers().get(new Random().nextInt(world.getPlayers().size())));
}
if (config.fallingStarsEnabled && new Random().nextDouble() <= fallingStarChance) {
CelestialSphere.createFallingStar(celeste,
world.getPlayers().get(new Random().nextInt(world.getPlayers().size())));
}
}
}
}
@@ -1,57 +0,0 @@
package com.idreesinc.celeste;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import com.idreesinc.celeste.commands.CommandCeleste;
import com.idreesinc.celeste.commands.CommandFallingStar;
import com.idreesinc.celeste.commands.CommandShootingStar;
import com.idreesinc.celeste.utilities.Metrics;
import com.idreesinc.celeste.utilities.UpdateChecker;
import com.idreesinc.celeste.config.CelesteConfigManager;
public class Celeste extends JavaPlugin {
public CelesteConfigManager configManager = new CelesteConfigManager(this);
@Override
public void onEnable() {
this.saveDefaultConfig();
// bStats metrics
Metrics metrics = new Metrics(this, 8292);
this.getCommand("celeste").setExecutor(new CommandCeleste(this));
this.getCommand("shootingstar").setExecutor(new CommandShootingStar(this));
this.getCommand("fallingstar").setExecutor(new CommandFallingStar(this));
configManager.processConfigs();
BukkitRunnable stargazingTask = new Astronomer(this);
stargazingTask.runTaskTimer(this, 0, 10);
checkForUpdates();
}
public void reload() {
reloadConfig();
configManager.processConfigs();
checkForUpdates();
}
public void checkForUpdates() {
if (this.getConfig().getBoolean("check-for-updates")) {
new UpdateChecker(this, 81862).getVersion(version -> {
try {
double current = Double.parseDouble(this.getDescription().getVersion());
double api = Double.parseDouble(version);
if (current < api) {
this.getLogger().info("There is an update available for Celeste (" + current + " -> " + api + ")");
}
} catch (NumberFormatException e) {
if (this.getConfig().getBoolean("debug")) {
this.getLogger().severe("Unable to process remote plugin version number '" + version + "'");
}
}
});
}
}
}
@@ -1,91 +0,0 @@
package com.idreesinc.celeste;
import com.idreesinc.celeste.config.CelesteConfig;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Random;
public class CelestialSphere {
public static void createShootingStar(Celeste celeste, Player player) {
createShootingStar(celeste, player, true);
}
public static void createShootingStar(Celeste celeste, Player player, boolean approximate) {
createShootingStar(celeste, player.getLocation(), approximate);
}
public static void createShootingStar(Celeste celeste, Location location) {
createShootingStar(celeste, location, true);
}
public static void createShootingStar(Celeste celeste, Location location, boolean approximate) {
Location starLocation;
CelesteConfig config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
double w = 100 * Math.sqrt(new Random().nextDouble());
double t = 2d * Math.PI * new Random().nextDouble();
double x = w * Math.cos(t);
double range = Math.max(0, config.shootingStarsMaxHeight - config.shootingStarsMinHeight);
double y = Math.max(new Random().nextDouble() * range + config.shootingStarsMinHeight, location.getY() + 50);
double z = w * Math.sin(t);
if (approximate) {
starLocation = new Location(location.getWorld(), location.getX() + x, y, location.getZ() + z);
} else {
starLocation = new Location(location.getWorld(), location.getX(), y, location.getZ());
}
Vector direction = new Vector(new Random().nextDouble() * 2 - 1, new Random().nextDouble() * -0.75d, new Random().nextDouble() * 2 - 1);
direction.normalize();
double speed = new Random().nextDouble() * 2 + 0.75;
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, starLocation, 0, direction.getX(),
direction.getY(), direction.getZ(), speed, null, true);
if (new Random().nextDouble() >= 0.5) {
location.getWorld().spawnParticle(Particle.EXPLOSION_NORMAL, starLocation, 0, direction.getX(),
direction.getY(), direction.getZ(), speed, null, true);
}
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Shooting star at " + stringifyLocation(starLocation) + " in world " + starLocation.getWorld().getName());
}
}
public static void createFallingStar(Celeste celeste, Player player) {
createFallingStar(celeste, player, true);
}
public static void createFallingStar(Celeste celeste, Player player, boolean approximate) {
createFallingStar(celeste, player.getLocation(), approximate);
}
public static void createFallingStar(Celeste celeste, final Location location) {
createFallingStar(celeste, location, true);
}
public static void createFallingStar(Celeste celeste, final Location location, boolean approximate) {
Location target = location;
CelesteConfig config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
if (approximate) {
double fallingStarRadius = config.fallingStarsRadius;
double w = fallingStarRadius * Math.sqrt(new Random().nextDouble());
double t = 2d * Math.PI * new Random().nextDouble();
double x = w * Math.cos(t);
double z = w * Math.sin(t);
target = new Location(location.getWorld(), location.getX() + x, location.getY(), location.getZ() + z);
}
BukkitRunnable fallingStarTask = new FallingStar(celeste, target);
fallingStarTask.runTaskTimer(celeste, 0, 1);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Falling star at " + stringifyLocation(target) + " in world " + target.getWorld().getName());
}
}
private static String stringifyLocation(Location location) {
DecimalFormat format = new DecimalFormat("##.00");
format.setRoundingMode(RoundingMode.HALF_UP);
return "x: " + format.format(location.getX()) + " y: " + format.format(location.getY()) + " z: " + format.format(location.getZ());
}
}
@@ -1,103 +0,0 @@
package com.idreesinc.celeste;
import com.idreesinc.celeste.config.CelesteConfig;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class FallingStar extends BukkitRunnable {
private final Celeste celeste;
private final Location location;
private final Location dropLoc;
private final CelesteConfig config;
private double y = 256;
private boolean soundPlayed = false;
private boolean lootDropped = false;
private int sparkTimer;
public FallingStar(Celeste celeste, Location location) {
this.celeste = celeste;
this.location = location;
config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
sparkTimer = config.fallingStarsSparkTime;
dropLoc = new Location(location.getWorld(), location.getX(),
location.getWorld().getHighestBlockAt(location).getY() + 1, location.getZ());
}
public void run() {
double step = 1;
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, location.getX(), y, location.getZ(),
0, 0, new Random().nextDouble(), 0,
0.2, null, true);
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, location.getX(),
y + new Random().nextDouble() * step,
location.getZ(),
0, 0, -1, 0,
1, null, true);
if (y % (step * 2) == 0) {
location.getWorld().spawnParticle(Particle.LAVA, location.getX(), y + new Random().nextDouble(),
location.getZ(),
0, 0, new Random().nextDouble(), 0,
0.2, null, true);
}
if (config.fallingStarsSoundEnabled && !soundPlayed && y <= dropLoc.getY() + 75) {
location.getWorld().playSound(dropLoc, Sound.BLOCK_BELL_RESONATE, (float) config.fallingStarsVolume, 0.5f);
soundPlayed = true;
}
if (y <= dropLoc.getY()) {
if (!lootDropped) {
// Note that both simple loot and loot tables will drop if both are configured because why not
if (config.fallingStarSimpleLoot != null && config.fallingStarSimpleLoot.entries.size() > 0) {
ItemStack drop = new ItemStack(Material.valueOf(config.fallingStarSimpleLoot.getRandom()), 1);
location.getWorld().dropItem(dropLoc, drop);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Spawned simple falling star loot");
}
}
if (config.fallingStarLootTable != null) {
// Armor stands are used as markers are not compatible with 1.14
Entity marker = dropLoc.getWorld().spawnEntity(location, EntityType.ARMOR_STAND);
String command = String.format("execute at %s run loot spawn %s %s %s loot %s",
marker.getUniqueId(),
dropLoc.getX(),
dropLoc.getY(),
dropLoc.getZ(),
config.fallingStarLootTable);
celeste.getServer().dispatchCommand(celeste.getServer().getConsoleSender(), command);
marker.remove();
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Spawned falling star loot from loot table '" + config.fallingStarLootTable + "'");
}
}
if (config.fallingStarsExperience > 0) {
ExperienceOrb orb = (ExperienceOrb) dropLoc.getWorld().spawnEntity(dropLoc, EntityType.EXPERIENCE_ORB);
orb.setExperience(config.fallingStarsExperience);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Dropping experience orbs with value " + config.fallingStarsExperience);
}
}
lootDropped = true;
}
if (y % (step * 5) == 0) {
location.getWorld().spawnParticle(Particle.LAVA, dropLoc,
0, 0, new Random().nextDouble(), 0,
1, null, true);
}
sparkTimer--;
if (sparkTimer <= 0) {
this.cancel();
}
}
y -= step;
}
}
@@ -1,39 +0,0 @@
package com.idreesinc.celeste.commands;
import com.idreesinc.celeste.Celeste;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandCeleste implements CommandExecutor {
Celeste celeste;
public CommandCeleste(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission("celeste.reload")) {
celeste.reload();
sender.sendMessage("Celeste has been reloaded");
} else {
sender.sendMessage("You do not have permission to use this command");
}
return true;
} else if (args.length == 1 && args[0].equalsIgnoreCase("info")) {
if (sender.hasPermission("celeste.info")) {
sender.sendMessage("Celeste v" + celeste.getDescription().getVersion());
sender.sendMessage("Shooting stars: " + (celeste.getConfig().getBoolean("shooting-stars-enabled") ? "Enabled" : "Disabled"));
sender.sendMessage("Falling stars: " + (celeste.getConfig().getBoolean("falling-stars-enabled") ? "Enabled" : "Disabled"));
sender.sendMessage("Meteor showers: " + (celeste.getConfig().getBoolean("new-moon-meteor-shower") ? "Enabled" : "Disabled"));
} else {
sender.sendMessage("You do not have permission to use this command");
}
return true;
}
return false;
}
}
@@ -1,41 +0,0 @@
package com.idreesinc.celeste.commands;
import com.idreesinc.celeste.Celeste;
import com.idreesinc.celeste.CelestialSphere;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CommandFallingStar implements CommandExecutor {
Celeste celeste;
public CommandFallingStar(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (Bukkit.getPlayer(args[0]) == null) {
sender.sendMessage("\u00a7cError: Player not found.");
return true;
}
CelestialSphere.createFallingStar(celeste, Bukkit.getPlayer(args[0]), false);
} else {
if (sender instanceof Player) {
Player player = (Player) sender;
CelestialSphere.createFallingStar(celeste, player, false);
} else {
return false;
}
}
String message = this.celeste.getConfig().getString("falling-stars-summon-text");
if (message != null) {
sender.sendMessage(message);
}
return true;
}
}
@@ -1,41 +0,0 @@
package com.idreesinc.celeste.commands;
import com.idreesinc.celeste.Celeste;
import com.idreesinc.celeste.CelestialSphere;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CommandShootingStar implements CommandExecutor {
Celeste celeste;
public CommandShootingStar(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (Bukkit.getPlayer(args[0]) == null) {
sender.sendMessage("\u00a7cError: Player not found.");
return true;
}
CelestialSphere.createShootingStar(celeste, Bukkit.getPlayer(args[0]), false);
} else {
if (sender instanceof Player) {
Player player = (Player) sender;
CelestialSphere.createShootingStar(celeste, player, false);
} else {
return false;
}
}
String message = this.celeste.getConfig().getString("shooting-stars-summon-text");
if (message != null) {
sender.sendMessage(message);
}
return true;
}
}
@@ -1,111 +0,0 @@
package com.idreesinc.celeste.config;
import com.idreesinc.celeste.utilities.WeightedRandomBag;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
public class CelesteConfig {
public boolean newMoonMeteorShower;
public int beginSpawningStarsTime;
public int endSpawningStarsTime;
public boolean shootingStarsEnabled;
public double shootingStarsPerMinute;
public double shootingStarsPerMinuteMeteorShower;
public int shootingStarsMinHeight;
public int shootingStarsMaxHeight;
public boolean fallingStarsEnabled;
public double fallingStarsPerMinute;
public double fallingStarsPerMinuteMeteorShower;
public int fallingStarsRadius;
public boolean fallingStarsSoundEnabled;
public double fallingStarsVolume;
public int fallingStarsSparkTime;
public int fallingStarsExperience;
public WeightedRandomBag<String> fallingStarSimpleLoot;
public String fallingStarLootTable;
public CelesteConfig(ConfigurationSection section) {
// Used to build the global config
buildFromConfigurationSection(section);
}
public CelesteConfig(ConfigurationSection section, CelesteConfig globalConfig) {
// Used to build per-world configs
buildFromConfigurationSectionWithGlobal(section, globalConfig);
}
private void buildFromConfigurationSection(ConfigurationSection section) {
newMoonMeteorShower = section.getBoolean("new-moon-meteor-shower");
beginSpawningStarsTime = section.getInt("begin-spawning-stars-time");
endSpawningStarsTime = section.getInt("end-spawning-stars-time");
shootingStarsEnabled = section.getBoolean("shooting-stars-enabled");
shootingStarsPerMinute = section.getDouble("shooting-stars-per-minute");
shootingStarsPerMinuteMeteorShower = section.getDouble("shooting-stars-per-minute-during-meteor-showers");
shootingStarsMinHeight = section.getInt("shooting-stars-min-height");
shootingStarsMaxHeight = section.getInt("shooting-stars-max-height");
fallingStarsEnabled = section.getBoolean("falling-stars-enabled");
fallingStarsPerMinute = section.getDouble("falling-stars-per-minute");
fallingStarsPerMinuteMeteorShower = section.getDouble("falling-stars-per-minute-during-meteor-showers");
fallingStarsRadius = section.getInt("falling-stars-radius");
fallingStarsSoundEnabled = section.getBoolean("falling-stars-sound-enabled");
fallingStarsVolume = section.getDouble("falling-stars-volume");
fallingStarsSparkTime = section.getInt("falling-stars-spark-time");
fallingStarsExperience = section.getInt("falling-stars-experience");
fallingStarLootTable = section.getString("falling-stars-loot-table");
if (section.isSet("falling-stars-loot")) {
fallingStarSimpleLoot = calculateSimpleLoot(section.getConfigurationSection("falling-stars-loot"));
}
}
private void buildFromConfigurationSectionWithGlobal(ConfigurationSection section, CelesteConfig globalConfig) {
newMoonMeteorShower = section.getBoolean("new-moon-meteor-shower", globalConfig.newMoonMeteorShower);
beginSpawningStarsTime = section.getInt("begin-spawning-stars-time", globalConfig.beginSpawningStarsTime);
endSpawningStarsTime = section.getInt("end-spawning-stars-time", globalConfig.endSpawningStarsTime);
shootingStarsEnabled = section.getBoolean("shooting-stars-enabled", globalConfig.shootingStarsEnabled);
shootingStarsPerMinute = section.getDouble("shooting-stars-per-minute", globalConfig.shootingStarsPerMinute);
shootingStarsPerMinuteMeteorShower = section.getDouble("shooting-stars-per-minute-during-meteor-showers", globalConfig.shootingStarsPerMinuteMeteorShower);
shootingStarsMinHeight = section.getInt("shooting-stars-min-height", globalConfig.shootingStarsMinHeight);
shootingStarsMaxHeight = section.getInt("shooting-stars-max-height", globalConfig.shootingStarsMaxHeight);
fallingStarsEnabled = section.getBoolean("falling-stars-enabled", globalConfig.fallingStarsEnabled);
fallingStarsPerMinute = section.getDouble("falling-stars-per-minute", globalConfig.fallingStarsPerMinute);
fallingStarsPerMinuteMeteorShower = section.getDouble("falling-stars-per-minute-during-meteor-showers", globalConfig.fallingStarsPerMinuteMeteorShower);
fallingStarsRadius = section.getInt("falling-stars-radius", globalConfig.fallingStarsRadius);
fallingStarsSoundEnabled = section.getBoolean("falling-stars-sound-enabled", globalConfig.fallingStarsSoundEnabled);
fallingStarsVolume = section.getDouble("falling-stars-volume", globalConfig.fallingStarsVolume);
fallingStarsSparkTime = section.getInt("falling-stars-spark-time", globalConfig.fallingStarsSparkTime);
fallingStarsExperience = section.getInt("falling-stars-experience", globalConfig.fallingStarsExperience);
if (section.isSet("falling-stars-loot") || section.isSet("falling-stars-loot-table")) {
// Ensure that neither type of loot is inherited from the global config if any overrides are used
// This means that even if the global config sets simple loot and the world config overrides just loot
// table, the end result will be a config with the loot table set and the simple table empty
fallingStarLootTable = section.getString("falling-stars-loot-table");
if (section.isSet("falling-stars-loot")) {
fallingStarSimpleLoot = calculateSimpleLoot(section.getConfigurationSection("falling-stars-loot"));
}
} else {
fallingStarLootTable = globalConfig.fallingStarLootTable;
fallingStarSimpleLoot = globalConfig.fallingStarSimpleLoot;
}
}
public WeightedRandomBag<String> calculateSimpleLoot(ConfigurationSection loot) {
WeightedRandomBag<String> fallingStarDrops = new WeightedRandomBag<>();
for (String key : loot.getKeys(false)) {
try {
Material.valueOf(key.toUpperCase());
fallingStarDrops.addEntry(key.toUpperCase(), loot.getDouble(key));
} catch (IllegalArgumentException e) {
System.err.println("Error: Item with name " + key.toUpperCase() + " does not exist, skipping");
}
}
return fallingStarDrops;
}
}
@@ -1,838 +0,0 @@
package com.idreesinc.celeste.utilities;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
public class Metrics {
private final Plugin plugin;
private final MetricsBase metricsBase;
/**
* Creates a new Metrics instance.
*
* @param plugin Your plugin instance.
* @param serviceId The id of the service. It can be found at <a
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(JavaPlugin plugin, int serviceId) {
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
if (!config.isSet("serverUuid")) {
config.addDefault("enabled", true);
config.addDefault("serverUuid", UUID.randomUUID().toString());
config.addDefault("logFailedRequests", false);
config.addDefault("logSentData", false);
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config
.options()
.header(
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
+ "anonymous.")
.copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) {
}
}
// Load the data
boolean enabled = config.getBoolean("enabled", true);
String serverUUID = config.getString("serverUuid");
boolean logErrors = config.getBoolean("logFailedRequests", false);
boolean logSentData = config.getBoolean("logSentData", false);
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
metricsBase =
new MetricsBase(
"bukkit",
serverUUID,
serviceId,
enabled,
this::appendPlatformData,
this::appendServiceData,
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
plugin::isEnabled,
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
(message) -> this.plugin.getLogger().log(Level.INFO, message),
logErrors,
logSentData,
logResponseStatusText);
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
metricsBase.addCustomChart(chart);
}
private void appendPlatformData(JsonObjectBuilder builder) {
builder.appendField("playerAmount", getPlayerAmount());
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
builder.appendField("bukkitVersion", Bukkit.getVersion());
builder.appendField("bukkitName", Bukkit.getName());
builder.appendField("javaVersion", System.getProperty("java.version"));
builder.appendField("osName", System.getProperty("os.name"));
builder.appendField("osArch", System.getProperty("os.arch"));
builder.appendField("osVersion", System.getProperty("os.version"));
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
}
private void appendServiceData(JsonObjectBuilder builder) {
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
}
private int getPlayerAmount() {
try {
// Around MC 1.8 the return type was changed from an array to a collection,
// This fixes java.lang.NoSuchMethodError:
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
return onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
// Just use the new method if the reflection failed
return Bukkit.getOnlinePlayers().size();
}
}
public static class MetricsBase {
/** The version of the Metrics class. */
public static final String METRICS_VERSION = "2.2.1";
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics"));
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
private final String platform;
private final String serverUuid;
private final int serviceId;
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
private final Consumer<Runnable> submitTaskConsumer;
private final Supplier<Boolean> checkServiceEnabledSupplier;
private final BiConsumer<String, Throwable> errorLogger;
private final Consumer<String> infoLogger;
private final boolean logErrors;
private final boolean logSentData;
private final boolean logResponseStatusText;
private final Set<CustomChart> customCharts = new HashSet<>();
private final boolean enabled;
/**
* Creates a new MetricsBase class instance.
*
* @param platform The platform of the service.
* @param serviceId The id of the service.
* @param serverUuid The server uuid.
* @param enabled Whether or not data sending is enabled.
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
* appends all platform-specific data.
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
* appends all service-specific data.
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
* used to delegate the data collection to a another thread to prevent errors caused by
* concurrency. Can be {@code null}.
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
* @param errorLogger A consumer that accepts log message and an error.
* @param infoLogger A consumer that accepts info log messages.
* @param logErrors Whether or not errors should be logged.
* @param logSentData Whether or not the sent data should be logged.
* @param logResponseStatusText Whether or not the response status text should be logged.
*/
public MetricsBase(
String platform,
String serverUuid,
int serviceId,
boolean enabled,
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
Consumer<Runnable> submitTaskConsumer,
Supplier<Boolean> checkServiceEnabledSupplier,
BiConsumer<String, Throwable> errorLogger,
Consumer<String> infoLogger,
boolean logErrors,
boolean logSentData,
boolean logResponseStatusText) {
this.platform = platform;
this.serverUuid = serverUuid;
this.serviceId = serviceId;
this.enabled = enabled;
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
this.appendServiceDataConsumer = appendServiceDataConsumer;
this.submitTaskConsumer = submitTaskConsumer;
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
this.errorLogger = errorLogger;
this.infoLogger = infoLogger;
this.logErrors = logErrors;
this.logSentData = logSentData;
this.logResponseStatusText = logResponseStatusText;
checkRelocation();
if (enabled) {
startSubmitting();
}
}
public void addCustomChart(CustomChart chart) {
this.customCharts.add(chart);
}
private void startSubmitting() {
final Runnable submitTask =
() -> {
if (!enabled || !checkServiceEnabledSupplier.get()) {
// Submitting data or service is disabled
scheduler.shutdown();
return;
}
if (submitTaskConsumer != null) {
submitTaskConsumer.accept(this::submitData);
} else {
this.submitData();
}
};
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution
// of requests on the
// bStats backend. To circumvent this problem, we introduce some randomness into the initial
// and second delay.
// WARNING: You must not modify and part of this Metrics class, including the submit delay or
// frequency!
// WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it!
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
}
private void submitData() {
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
appendPlatformDataConsumer.accept(baseJsonBuilder);
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
appendServiceDataConsumer.accept(serviceJsonBuilder);
JsonObjectBuilder.JsonObject[] chartData =
customCharts.stream()
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
.filter(Objects::nonNull)
.toArray(JsonObjectBuilder.JsonObject[]::new);
serviceJsonBuilder.appendField("id", serviceId);
serviceJsonBuilder.appendField("customCharts", chartData);
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
baseJsonBuilder.appendField("serverUUID", serverUuid);
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
scheduler.execute(
() -> {
try {
// Send the data
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logErrors) {
errorLogger.accept("Could not submit bStats metrics data", e);
}
}
});
}
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
if (logSentData) {
infoLogger.accept("Sent bStats metrics data: " + data.toString());
}
String url = String.format(REPORT_URL, platform);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip");
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
infoLogger.accept("Sent data to bStats and received response: " + builder);
}
}
/** Checks that the class was properly relocated. */
private void checkRelocation() {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little
// "trick" ... :D
final String defaultPackage =
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
final String examplePackage =
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure no one just copy & pastes the example and uses the wrong package
// names
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
/**
* Gzips the given string.
*
* @param str The string to gzip.
* @return The gzipped string.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
}
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
// Skip this invalid
continue;
}
allSkipped = false;
valuesBuilder.appendField(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
public abstract static class CustomChart {
private final String chartId;
protected CustomChart(String chartId) {
if (chartId == null) {
throw new IllegalArgumentException("chartId must not be null");
}
this.chartId = chartId;
}
public JsonObjectBuilder.JsonObject getRequestJsonObject(
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
JsonObjectBuilder builder = new JsonObjectBuilder();
builder.appendField("chartId", chartId);
try {
JsonObjectBuilder.JsonObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
builder.appendField("data", data);
} catch (Throwable t) {
if (logErrors) {
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return builder.build();
}
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
}
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("value", value).build();
}
}
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("value", value).build();
}
}
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
}
}
/**
* An extremely simple JSON builder.
*
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
* for its use-case.
*/
public static class JsonObjectBuilder {
private StringBuilder builder = new StringBuilder();
private boolean hasAtLeastOneField = false;
public JsonObjectBuilder() {
builder.append("{");
}
/**
* Appends a null field to the JSON.
*
* @param key The key of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendNull(String key) {
appendFieldUnescaped(key, "null");
return this;
}
/**
* Appends a string field to the JSON.
*
* @param key The key of the field.
* @param value The value of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, String value) {
if (value == null) {
throw new IllegalArgumentException("JSON value must not be null");
}
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
return this;
}
/**
* Appends an integer field to the JSON.
*
* @param key The key of the field.
* @param value The value of the field.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, int value) {
appendFieldUnescaped(key, String.valueOf(value));
return this;
}
/**
* Appends an object to the JSON.
*
* @param key The key of the field.
* @param object The object.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, JsonObject object) {
if (object == null) {
throw new IllegalArgumentException("JSON object must not be null");
}
appendFieldUnescaped(key, object.toString());
return this;
}
/**
* Appends a string array to the JSON.
*
* @param key The key of the field.
* @param values The string array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, String[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values)
.map(value -> "\"" + escape(value) + "\"")
.collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends an integer array to the JSON.
*
* @param key The key of the field.
* @param values The integer array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, int[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends an object array to the JSON.
*
* @param key The key of the field.
* @param values The integer array.
* @return A reference to this object.
*/
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
if (values == null) {
throw new IllegalArgumentException("JSON values must not be null");
}
String escapedValues =
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
appendFieldUnescaped(key, "[" + escapedValues + "]");
return this;
}
/**
* Appends a field to the object.
*
* @param key The key of the field.
* @param escapedValue The escaped value of the field.
*/
private void appendFieldUnescaped(String key, String escapedValue) {
if (builder == null) {
throw new IllegalStateException("JSON has already been built");
}
if (key == null) {
throw new IllegalArgumentException("JSON key must not be null");
}
if (hasAtLeastOneField) {
builder.append(",");
}
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
hasAtLeastOneField = true;
}
/**
* Builds the JSON string and invalidates this builder.
*
* @return The built JSON string.
*/
public JsonObject build() {
if (builder == null) {
throw new IllegalStateException("JSON has already been built");
}
JsonObject object = new JsonObject(builder.append("}").toString());
builder = null;
return object;
}
/**
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
*
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
*
* @param value The value to escape.
* @return The escaped value.
*/
private static String escape(String value) {
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '"') {
builder.append("\\\"");
} else if (c == '\\') {
builder.append("\\\\");
} else if (c <= '\u000F') {
builder.append("\\u000").append(Integer.toHexString(c));
} else if (c <= '\u001F') {
builder.append("\\u00").append(Integer.toHexString(c));
} else {
builder.append(c);
}
}
return builder.toString();
}
/**
* A super simple representation of a JSON object.
*
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
* JsonObject)}.
*/
public static class JsonObject {
private final String value;
private JsonObject(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
}
}
@@ -1,34 +0,0 @@
package com.idreesinc.celeste.utilities;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Consumer;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
public class UpdateChecker {
private Plugin plugin;
private int resourceId;
public UpdateChecker(Plugin plugin, int resourceId) {
this.plugin = plugin;
this.resourceId = resourceId;
}
public void getVersion(final Consumer<String> consumer) {
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
try (InputStream inputStream = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream(); Scanner scanner = new Scanner(inputStream)) {
if (scanner.hasNext()) {
consumer.accept(scanner.next());
}
} catch (IOException exception) {
this.plugin.getLogger().info("Cannot look for updates: " + exception.getMessage());
}
});
}
}
@@ -1,37 +0,0 @@
package com.idreesinc.celeste.utilities;
//From https://gamedev.stackexchange.com/a/162987
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class WeightedRandomBag<T extends Object> {
private class Entry {
double accumulatedWeight;
T object;
}
public List<Entry> entries = new ArrayList<Entry>();
private double accumulatedWeight;
private Random rand = new Random();
public void addEntry(T object, double weight) {
accumulatedWeight += weight;
Entry e = new Entry();
e.object = object;
e.accumulatedWeight = accumulatedWeight;
entries.add(e);
}
public T getRandom() {
double r = rand.nextDouble() * accumulatedWeight;
for (Entry entry: entries) {
if (entry.accumulatedWeight >= r) {
return entry.object;
}
}
// Should only happen when there are no entries
return null;
}
}
@@ -0,0 +1,61 @@
package net.trueog.celeste;
import java.util.List;
import java.util.Random;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
import net.trueog.celeste.config.CelesteConfig;
public class Astronomer extends BukkitRunnable {
private final Celeste celeste;
public Astronomer(Celeste celeste) {
this.celeste = celeste;
}
public void run() {
if (celeste.getServer().getOnlinePlayers().size() == 0) {
return;
}
List<World> worlds = celeste.getServer().getWorlds();
for (World world : worlds) {
CelesteConfig config = celeste.configManager.getConfigForWorld(world.getName());
if (!celeste.configManager.doesWorldHaveOverrides(world.getName())
&& !world.getEnvironment().equals(World.Environment.NORMAL)) {
// Ensure that Celeste only runs on normal worlds unless override is specified in config
continue;
}
if (world.getPlayers().size() == 0) {
continue;
}
if (!(world.getTime() >= config.beginSpawningStarsTime && world.getTime() <= config.endSpawningStarsTime)) {
continue;
}
if (world.hasStorm()) {
continue;
}
double shootingStarChance;
double fallingStarChance;
if (config.newMoonMeteorShower && (world.getFullTime() / 24000) % 8 == 4) {
shootingStarChance = config.shootingStarsPerMinuteMeteorShower / 120d;
fallingStarChance = config.fallingStarsPerMinuteMeteorShower / 120d;
} else {
shootingStarChance = config.shootingStarsPerMinute / 120d;
fallingStarChance = config.fallingStarsPerMinute / 120d;
}
if (config.shootingStarsEnabled && new Random().nextDouble() <= shootingStarChance) {
CelestialSphere.createShootingStar(celeste,
world.getPlayers().get(new Random().nextInt(world.getPlayers().size())));
}
if (config.fallingStarsEnabled && new Random().nextDouble() <= fallingStarChance) {
CelestialSphere.createFallingStar(celeste,
world.getPlayers().get(new Random().nextInt(world.getPlayers().size())));
}
}
}
}
@@ -0,0 +1,34 @@
package net.trueog.celeste;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import net.trueog.celeste.commands.CommandCeleste;
import net.trueog.celeste.commands.CommandFallingStar;
import net.trueog.celeste.commands.CommandShootingStar;
import net.trueog.celeste.config.CelesteConfigManager;
public class Celeste extends JavaPlugin {
public CelesteConfigManager configManager = new CelesteConfigManager(this);
@Override
public void onEnable() {
this.saveDefaultConfig();
this.getCommand("celeste").setExecutor(new CommandCeleste(this));
this.getCommand("shootingstar").setExecutor(new CommandShootingStar(this));
this.getCommand("fallingstar").setExecutor(new CommandFallingStar(this));
configManager.processConfigs();
BukkitRunnable stargazingTask = new Astronomer(this);
stargazingTask.runTaskTimer(this, 0, 10);
}
public void reload() {
reloadConfig();
configManager.processConfigs();
}
}
@@ -0,0 +1,92 @@
package net.trueog.celeste;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Random;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import net.trueog.celeste.config.CelesteConfig;
public class CelestialSphere {
public static void createShootingStar(Celeste celeste, Player player) {
createShootingStar(celeste, player, true);
}
public static void createShootingStar(Celeste celeste, Player player, boolean approximate) {
createShootingStar(celeste, player.getLocation(), approximate);
}
public static void createShootingStar(Celeste celeste, Location location) {
createShootingStar(celeste, location, true);
}
public static void createShootingStar(Celeste celeste, Location location, boolean approximate) {
Location starLocation;
CelesteConfig config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
double w = 100 * Math.sqrt(new Random().nextDouble());
double t = 2d * Math.PI * new Random().nextDouble();
double x = w * Math.cos(t);
double range = Math.max(0, config.shootingStarsMaxHeight - config.shootingStarsMinHeight);
double y = Math.max(new Random().nextDouble() * range + config.shootingStarsMinHeight, location.getY() + 50);
double z = w * Math.sin(t);
if (approximate) {
starLocation = new Location(location.getWorld(), location.getX() + x, y, location.getZ() + z);
} else {
starLocation = new Location(location.getWorld(), location.getX(), y, location.getZ());
}
Vector direction = new Vector(new Random().nextDouble() * 2 - 1, new Random().nextDouble() * -0.75d, new Random().nextDouble() * 2 - 1);
direction.normalize();
double speed = new Random().nextDouble() * 2 + 0.75;
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, starLocation, 0, direction.getX(),
direction.getY(), direction.getZ(), speed, null, true);
if (new Random().nextDouble() >= 0.5) {
location.getWorld().spawnParticle(Particle.EXPLOSION_NORMAL, starLocation, 0, direction.getX(),
direction.getY(), direction.getZ(), speed, null, true);
}
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Shooting star at " + stringifyLocation(starLocation) + " in world " + starLocation.getWorld().getName());
}
}
public static void createFallingStar(Celeste celeste, Player player) {
createFallingStar(celeste, player, true);
}
public static void createFallingStar(Celeste celeste, Player player, boolean approximate) {
createFallingStar(celeste, player.getLocation(), approximate);
}
public static void createFallingStar(Celeste celeste, final Location location) {
createFallingStar(celeste, location, true);
}
public static void createFallingStar(Celeste celeste, final Location location, boolean approximate) {
Location target = location;
CelesteConfig config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
if (approximate) {
double fallingStarRadius = config.fallingStarsRadius;
double w = fallingStarRadius * Math.sqrt(new Random().nextDouble());
double t = 2d * Math.PI * new Random().nextDouble();
double x = w * Math.cos(t);
double z = w * Math.sin(t);
target = new Location(location.getWorld(), location.getX() + x, location.getY(), location.getZ() + z);
}
BukkitRunnable fallingStarTask = new FallingStar(celeste, target);
fallingStarTask.runTaskTimer(celeste, 0, 1);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Falling star at " + stringifyLocation(target) + " in world " + target.getWorld().getName());
}
}
private static String stringifyLocation(Location location) {
DecimalFormat format = new DecimalFormat("##.00");
format.setRoundingMode(RoundingMode.HALF_UP);
return "x: " + format.format(location.getX()) + " y: " + format.format(location.getY()) + " z: " + format.format(location.getZ());
}
}
@@ -0,0 +1,104 @@
package net.trueog.celeste;
import java.util.Random;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import net.trueog.celeste.config.CelesteConfig;
public class FallingStar extends BukkitRunnable {
private final Celeste celeste;
private final Location location;
private final Location dropLoc;
private final CelesteConfig config;
private double y = 256;
private boolean soundPlayed = false;
private boolean lootDropped = false;
private int sparkTimer;
public FallingStar(Celeste celeste, Location location) {
this.celeste = celeste;
this.location = location;
config = celeste.configManager.getConfigForWorld(location.getWorld().getName());
sparkTimer = config.fallingStarsSparkTime;
dropLoc = new Location(location.getWorld(), location.getX(),
location.getWorld().getHighestBlockAt(location).getY() + 1, location.getZ());
}
public void run() {
double step = 1;
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, location.getX(), y, location.getZ(),
0, 0, new Random().nextDouble(), 0,
0.2, null, true);
location.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, location.getX(),
y + new Random().nextDouble() * step,
location.getZ(),
0, 0, -1, 0,
1, null, true);
if (y % (step * 2) == 0) {
location.getWorld().spawnParticle(Particle.LAVA, location.getX(), y + new Random().nextDouble(),
location.getZ(),
0, 0, new Random().nextDouble(), 0,
0.2, null, true);
}
if (config.fallingStarsSoundEnabled && !soundPlayed && y <= dropLoc.getY() + 75) {
location.getWorld().playSound(dropLoc, Sound.BLOCK_BELL_RESONATE, (float) config.fallingStarsVolume, 0.5f);
soundPlayed = true;
}
if (y <= dropLoc.getY()) {
if (!lootDropped) {
// Note that both simple loot and loot tables will drop if both are configured because why not
if (config.fallingStarSimpleLoot != null && config.fallingStarSimpleLoot.entries.size() > 0) {
ItemStack drop = new ItemStack(Material.valueOf(config.fallingStarSimpleLoot.getRandom()), 1);
location.getWorld().dropItem(dropLoc, drop);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Spawned simple falling star loot");
}
}
if (config.fallingStarLootTable != null) {
// Armor stands are used as markers are not compatible with 1.14
Entity marker = dropLoc.getWorld().spawnEntity(location, EntityType.ARMOR_STAND);
String command = String.format("execute at %s run loot spawn %s %s %s loot %s",
marker.getUniqueId(),
dropLoc.getX(),
dropLoc.getY(),
dropLoc.getZ(),
config.fallingStarLootTable);
celeste.getServer().dispatchCommand(celeste.getServer().getConsoleSender(), command);
marker.remove();
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Spawned falling star loot from loot table '" + config.fallingStarLootTable + "'");
}
}
if (config.fallingStarsExperience > 0) {
ExperienceOrb orb = (ExperienceOrb) dropLoc.getWorld().spawnEntity(dropLoc, EntityType.EXPERIENCE_ORB);
orb.setExperience(config.fallingStarsExperience);
if (celeste.getConfig().getBoolean("debug")) {
celeste.getLogger().info("Dropping experience orbs with value " + config.fallingStarsExperience);
}
}
lootDropped = true;
}
if (y % (step * 5) == 0) {
location.getWorld().spawnParticle(Particle.LAVA, dropLoc,
0, 0, new Random().nextDouble(), 0,
1, null, true);
}
sparkTimer--;
if (sparkTimer <= 0) {
this.cancel();
}
}
y -= step;
}
}
@@ -0,0 +1,41 @@
package net.trueog.celeste.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import net.trueog.celeste.Celeste;
public class CommandCeleste implements CommandExecutor {
Celeste celeste;
public CommandCeleste(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission("celeste.reload")) {
celeste.reload();
sender.sendMessage("Celeste has been reloaded");
} else {
sender.sendMessage("You do not have permission to use this command");
}
return true;
} else if (args.length == 1 && args[0].equalsIgnoreCase("info")) {
String version = "1.0";
if (sender.hasPermission("celeste.info")) {
sender.sendMessage("Celeste-OG" + version);
sender.sendMessage("Shooting stars: " + (celeste.getConfig().getBoolean("shooting-stars-enabled") ? "Enabled" : "Disabled"));
sender.sendMessage("Falling stars: " + (celeste.getConfig().getBoolean("falling-stars-enabled") ? "Enabled" : "Disabled"));
sender.sendMessage("Meteor showers: " + (celeste.getConfig().getBoolean("new-moon-meteor-shower") ? "Enabled" : "Disabled"));
} else {
sender.sendMessage("You do not have permission to use this command");
}
return true;
}
return false;
}
}
@@ -0,0 +1,42 @@
package net.trueog.celeste.commands;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import net.trueog.celeste.Celeste;
import net.trueog.celeste.CelestialSphere;
public class CommandFallingStar implements CommandExecutor {
Celeste celeste;
public CommandFallingStar(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (Bukkit.getPlayer(args[0]) == null) {
sender.sendMessage("\u00a7cError: Player not found.");
return true;
}
CelestialSphere.createFallingStar(celeste, Bukkit.getPlayer(args[0]), false);
} else {
if (sender instanceof Player) {
Player player = (Player) sender;
CelestialSphere.createFallingStar(celeste, player, false);
} else {
return false;
}
}
String message = this.celeste.getConfig().getString("falling-stars-summon-text");
if (message != null) {
sender.sendMessage(message);
}
return true;
}
}
@@ -0,0 +1,42 @@
package net.trueog.celeste.commands;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import net.trueog.celeste.Celeste;
import net.trueog.celeste.CelestialSphere;
public class CommandShootingStar implements CommandExecutor {
Celeste celeste;
public CommandShootingStar(Celeste celeste) {
this.celeste = celeste;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (Bukkit.getPlayer(args[0]) == null) {
sender.sendMessage("\u00a7cError: Player not found.");
return true;
}
CelestialSphere.createShootingStar(celeste, Bukkit.getPlayer(args[0]), false);
} else {
if (sender instanceof Player) {
Player player = (Player) sender;
CelestialSphere.createShootingStar(celeste, player, false);
} else {
return false;
}
}
String message = this.celeste.getConfig().getString("shooting-stars-summon-text");
if (message != null) {
sender.sendMessage(message);
}
return true;
}
}
@@ -0,0 +1,112 @@
package net.trueog.celeste.config;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import net.trueog.celeste.utilities.WeightedRandomBag;
public class CelesteConfig {
public boolean newMoonMeteorShower;
public int beginSpawningStarsTime;
public int endSpawningStarsTime;
public boolean shootingStarsEnabled;
public double shootingStarsPerMinute;
public double shootingStarsPerMinuteMeteorShower;
public int shootingStarsMinHeight;
public int shootingStarsMaxHeight;
public boolean fallingStarsEnabled;
public double fallingStarsPerMinute;
public double fallingStarsPerMinuteMeteorShower;
public int fallingStarsRadius;
public boolean fallingStarsSoundEnabled;
public double fallingStarsVolume;
public int fallingStarsSparkTime;
public int fallingStarsExperience;
public WeightedRandomBag<String> fallingStarSimpleLoot;
public String fallingStarLootTable;
public CelesteConfig(ConfigurationSection section) {
// Used to build the global config
buildFromConfigurationSection(section);
}
public CelesteConfig(ConfigurationSection section, CelesteConfig globalConfig) {
// Used to build per-world configs
buildFromConfigurationSectionWithGlobal(section, globalConfig);
}
private void buildFromConfigurationSection(ConfigurationSection section) {
newMoonMeteorShower = section.getBoolean("new-moon-meteor-shower");
beginSpawningStarsTime = section.getInt("begin-spawning-stars-time");
endSpawningStarsTime = section.getInt("end-spawning-stars-time");
shootingStarsEnabled = section.getBoolean("shooting-stars-enabled");
shootingStarsPerMinute = section.getDouble("shooting-stars-per-minute");
shootingStarsPerMinuteMeteorShower = section.getDouble("shooting-stars-per-minute-during-meteor-showers");
shootingStarsMinHeight = section.getInt("shooting-stars-min-height");
shootingStarsMaxHeight = section.getInt("shooting-stars-max-height");
fallingStarsEnabled = section.getBoolean("falling-stars-enabled");
fallingStarsPerMinute = section.getDouble("falling-stars-per-minute");
fallingStarsPerMinuteMeteorShower = section.getDouble("falling-stars-per-minute-during-meteor-showers");
fallingStarsRadius = section.getInt("falling-stars-radius");
fallingStarsSoundEnabled = section.getBoolean("falling-stars-sound-enabled");
fallingStarsVolume = section.getDouble("falling-stars-volume");
fallingStarsSparkTime = section.getInt("falling-stars-spark-time");
fallingStarsExperience = section.getInt("falling-stars-experience");
fallingStarLootTable = section.getString("falling-stars-loot-table");
if (section.isSet("falling-stars-loot")) {
fallingStarSimpleLoot = calculateSimpleLoot(section.getConfigurationSection("falling-stars-loot"));
}
}
private void buildFromConfigurationSectionWithGlobal(ConfigurationSection section, CelesteConfig globalConfig) {
newMoonMeteorShower = section.getBoolean("new-moon-meteor-shower", globalConfig.newMoonMeteorShower);
beginSpawningStarsTime = section.getInt("begin-spawning-stars-time", globalConfig.beginSpawningStarsTime);
endSpawningStarsTime = section.getInt("end-spawning-stars-time", globalConfig.endSpawningStarsTime);
shootingStarsEnabled = section.getBoolean("shooting-stars-enabled", globalConfig.shootingStarsEnabled);
shootingStarsPerMinute = section.getDouble("shooting-stars-per-minute", globalConfig.shootingStarsPerMinute);
shootingStarsPerMinuteMeteorShower = section.getDouble("shooting-stars-per-minute-during-meteor-showers", globalConfig.shootingStarsPerMinuteMeteorShower);
shootingStarsMinHeight = section.getInt("shooting-stars-min-height", globalConfig.shootingStarsMinHeight);
shootingStarsMaxHeight = section.getInt("shooting-stars-max-height", globalConfig.shootingStarsMaxHeight);
fallingStarsEnabled = section.getBoolean("falling-stars-enabled", globalConfig.fallingStarsEnabled);
fallingStarsPerMinute = section.getDouble("falling-stars-per-minute", globalConfig.fallingStarsPerMinute);
fallingStarsPerMinuteMeteorShower = section.getDouble("falling-stars-per-minute-during-meteor-showers", globalConfig.fallingStarsPerMinuteMeteorShower);
fallingStarsRadius = section.getInt("falling-stars-radius", globalConfig.fallingStarsRadius);
fallingStarsSoundEnabled = section.getBoolean("falling-stars-sound-enabled", globalConfig.fallingStarsSoundEnabled);
fallingStarsVolume = section.getDouble("falling-stars-volume", globalConfig.fallingStarsVolume);
fallingStarsSparkTime = section.getInt("falling-stars-spark-time", globalConfig.fallingStarsSparkTime);
fallingStarsExperience = section.getInt("falling-stars-experience", globalConfig.fallingStarsExperience);
if (section.isSet("falling-stars-loot") || section.isSet("falling-stars-loot-table")) {
// Ensure that neither type of loot is inherited from the global config if any overrides are used
// This means that even if the global config sets simple loot and the world config overrides just loot
// table, the end result will be a config with the loot table set and the simple table empty
fallingStarLootTable = section.getString("falling-stars-loot-table");
if (section.isSet("falling-stars-loot")) {
fallingStarSimpleLoot = calculateSimpleLoot(section.getConfigurationSection("falling-stars-loot"));
}
} else {
fallingStarLootTable = globalConfig.fallingStarLootTable;
fallingStarSimpleLoot = globalConfig.fallingStarSimpleLoot;
}
}
public WeightedRandomBag<String> calculateSimpleLoot(ConfigurationSection loot) {
WeightedRandomBag<String> fallingStarDrops = new WeightedRandomBag<>();
for (String key : loot.getKeys(false)) {
try {
Material.valueOf(key.toUpperCase());
fallingStarDrops.addEntry(key.toUpperCase(), loot.getDouble(key));
} catch (IllegalArgumentException e) {
System.err.println("Error: Item with name " + key.toUpperCase() + " does not exist, skipping");
}
}
return fallingStarDrops;
}
}
@@ -1,14 +1,15 @@
package com.idreesinc.celeste.config;
import com.idreesinc.celeste.Celeste;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
package net.trueog.celeste.config;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import net.trueog.celeste.Celeste;
public class CelesteConfigManager {
private final Celeste celeste;
private final Map<String, CelesteConfig> worldConfigs = new HashMap<>();
private CelesteConfig globalConfig;
@@ -16,7 +17,7 @@ public class CelesteConfigManager {
public CelesteConfigManager(Celeste celeste) {
this.celeste = celeste;
}
public void processConfigs() {
this.worldConfigs.clear();
FileConfiguration config = this.celeste.getConfig();
@@ -0,0 +1,37 @@
package net.trueog.celeste.utilities;
//From https://gamedev.stackexchange.com/a/162987
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class WeightedRandomBag<T extends Object> {
private class Entry {
double accumulatedWeight;
T object;
}
public List<Entry> entries = new ArrayList<Entry>();
private double accumulatedWeight;
private Random rand = new Random();
public void addEntry(T object, double weight) {
accumulatedWeight += weight;
Entry e = new Entry();
e.object = object;
e.accumulatedWeight = accumulatedWeight;
entries.add(e);
}
public T getRandom() {
double r = rand.nextDouble() * accumulatedWeight;
for (Entry entry: entries) {
if (entry.accumulatedWeight >= r) {
return entry.object;
}
}
// Should only happen when there are no entries
return null;
}
}
-1
View File
@@ -1,4 +1,3 @@
check-for-updates: true
shooting-stars-enabled: true
falling-stars-enabled: true
begin-spawning-stars-time: 13000
+6 -6
View File
@@ -1,8 +1,8 @@
name: Celeste
version: 2.0
api-version: 1.14
author: Idrees
main: com.idreesinc.celeste.Celeste
name: Celeste-OG
version: 1.0
api-version: 1.19
authors: [NotAlexNoyle, Idrees]
main: net.trueog.celeste.Celeste
commands:
celeste:
@@ -19,7 +19,7 @@ commands:
permissions:
celeste.*:
description: Gives access to all Celeste commands
description: Gives access to all Celeste-OG commands
children:
celeste.reload: true
celeste.info: true