From 9b961b4383f243c804ba9e9272da9ce5db730431 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Thu, 2 Oct 2025 16:25:26 +0200 Subject: [PATCH 01/35] Add a gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 248 +++++++++++++++++++++++ gradlew.bat | 93 +++++++++ 4 files changed, 348 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8bdaf60c75ab801e22807dde59e12a8735a34077 GIT binary patch literal 45457 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 c82=K%QXLKFx%@O{wJjEi6Y56o#$)Bpeg literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7705927 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..adff685 --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/platforms/jvm/plugins-application/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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e509b2d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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 From adb149213f3220674506d5a0c2d4e32a059e5f51 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Thu, 2 Oct 2025 17:48:22 +0200 Subject: [PATCH 02/35] Paper, Java 21, Folia supported, real lights server side that melt snow. --- build.gradle.kts | 22 ++- .../dynamicLights/util/LightManager.java | 161 ++++++++++-------- src/main/resources/plugin.yml | 5 +- 3 files changed, 104 insertions(+), 84 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ef8c6c2..1aa8c72 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,15 +7,14 @@ plugins { } group = "github.xCykrix" -version = "1.3.0-FOR-MC1.20" +version = "1.3.2" repositories { + mavenLocal() mavenCentral() - // Spigot Repository - maven { - url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") - } + // Paper + maven("https://repo.papermc.io/repository/maven-public/") // Upstream GitHub Packages maven { @@ -28,8 +27,9 @@ repositories { } dependencies { - compileOnly("org.spigotmc:spigot-api:1.21.6-R0.1-SNAPSHOT") - implementation("github.xCykrix:spigotdevkit:1.1.0-CAPI1.20") { + // compileOnly("org.spigotmc:spigot-api:1.21.6-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.21.8-R0.1-SNAPSHOT") + implementation("github.xCykrix:spigotdevkit:1.1.0") { isTransitive = false } } @@ -39,8 +39,14 @@ tasks.named("shadowJar") { archiveClassifier = null; } +tasks { + assemble { + dependsOn(shadowJar) + } +} + // Target Java Build (Java 16 - Minecraft 1.17.x) -val targetJavaVersion = 16 +val targetJavaVersion = 21 java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index f71dda7..44bb441 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -3,11 +3,20 @@ import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.DynamicLights; import github.xCykrix.extendable.DevkitFullState; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import dist.xCykrix.shade.dev.dejvokep.boostedyaml.YamlDocument; import dist.xCykrix.shade.org.h2.mvstore.MVMap; +import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.HashSet; +import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -16,11 +25,10 @@ import org.bukkit.block.data.type.Light; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitTask; public class LightManager extends DevkitFullState { private final LightSource source; - private final HashMap tasks = new HashMap<>(); + private final HashMap tasks = new HashMap<>(); private final HashMap lastLightLocation = new HashMap<>(); public final MVMap toggles; @@ -28,6 +36,8 @@ public class LightManager extends DevkitFullState { private final long refresh; private final int distance; public final boolean toggle; + + private final List lights; public LightManager(DevkitPlugin plugin) { super(plugin); @@ -42,6 +52,11 @@ public LightManager(DevkitPlugin plugin) { this.refresh = config.getLong("update-rate"); this.distance = config.getInt("light-culling-distance"); this.toggle = config.getBoolean("default-toggle-state"); + + this.lights = Collections.synchronizedList(new LinkedList<>()); + + // @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelay, long period, @NotNull TimeUnit unit); + Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); } @Override @@ -50,63 +65,66 @@ public void initialize() { @Override public void shutdown() { - synchronized (this.tasks) { - for (UUID uuid : this.tasks.keySet()) { - this.tasks.get(uuid).cancel(); + clearLight(); + } + + public void addPlayer(Player player) {} + + public void tick() { + if(!plugin.isEnabled()){ + clearLight(); + return; + } + + Set actualLocations = new HashSet<>(); + + for (Player targetPlayer : Bukkit.getOnlinePlayers()) { + Optional opLocation = run(targetPlayer); + if(opLocation.isPresent()) { + actualLocations.add(opLocation.get()); } - this.tasks.clear(); + } + + // Remove all old light except for the one in actualLocations + synchronized (this.lights) { + for (Location location : this.lights) { + if (!actualLocations.contains(location)) { + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); + } + } + this.lights.clear(); + this.lights.addAll(actualLocations); } } - public void addPlayer(Player player) { - synchronized (this.tasks) { - if (this.tasks.containsKey(player.getUniqueId())) { - return; + public void clearLight() { + synchronized (this.lights) { + for (Location location : lights) { + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); } - this.tasks.put(player.getUniqueId(), - this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, () -> { - Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); - Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); - Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); - Material chestplate = getMaterialOrAir(player.getInventory().getChestplate()); - Material legging = getMaterialOrAir(player.getInventory().getLeggings()); - Material boot = getMaterialOrAir(player.getInventory().getBoots()); - boolean valid = this.valid(player, mainHand, offHand, helmet, - chestplate, legging, boot); - int lightLevel = 0; - if (valid) { - lightLevel = source.getLightLevel(mainHand, offHand, helmet, - chestplate, legging, boot); - } - for (Player targetPlayer : Bukkit.getOnlinePlayers()) { - String locationId = player.getUniqueId() + "/" + targetPlayer.getUniqueId(); - Location lastLocation = this.getLastLocation(locationId); - if (!valid) { - if (lastLocation != null) { - this.removeLight(targetPlayer, lastLocation); - this.removeLastLocation(locationId); - } - continue; - } - Location nextLocation = player.getEyeLocation(); - if (this.toggles.getOrDefault(targetPlayer.getUniqueId().toString(), this.toggle)) { - if (lightLevel > 0 && differentLocations(lastLocation, nextLocation)) { - if (player.getWorld().getName().equals(targetPlayer.getWorld().getName())) { - if (player.getLocation().distance(targetPlayer.getLocation()) <= this.distance) { - this.addLight(targetPlayer, nextLocation, lightLevel); - this.setLastLocation(locationId, nextLocation); - } - } - } - } - if (lastLocation != null && differentLocations(lastLocation, nextLocation)) { - this.removeLight(targetPlayer, lastLocation); - } - } - }, 50L, refresh)); } } + private Optional run(Player player) { + if(player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR){ + return Optional.empty(); + } + + Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); + Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); + Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); + Material chestplate = getMaterialOrAir(player.getInventory().getChestplate()); + Material legging = getMaterialOrAir(player.getInventory().getLeggings()); + Material boot = getMaterialOrAir(player.getInventory().getBoots()); + int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); + if (lightLevel > 0) { + Location eyeLocation = player.getEyeLocation(); + Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(player.getEyeLocation(), lightLevel)); + return Optional.of(eyeLocation); + } + return Optional.empty(); + } + public void removePlayer(UUID uid) { synchronized (this.tasks) { if (this.tasks.containsKey(uid)) { @@ -116,15 +134,21 @@ public void removePlayer(UUID uid) { } } - public void addLight(Player player, Location location, int lightLevel) { + public void addLight(Location location, int lightLevel) { if (lightLevel == 0) { return; } - Light light = (Light) Material.LIGHT.createBlockData(); - if (location.getWorld() == null) { - location.setWorld(player.getWorld()); - } World world = location.getWorld(); + Block block = world.getBlockAt(location); + //Only AIR or LIGHT can be replaced. + if(block.getType() != Material.AIR && block.getType() != Material.LIGHT) { + return; + } + if(block.getBlockData() instanceof Light lightData && lightData.getLevel() == lightLevel) { + return; + } + + Light light = (Light) Material.LIGHT.createBlockData(); switch (world.getBlockAt(location).getType()) { case AIR, CAVE_AIR -> { light.setWaterlogged(false); @@ -137,14 +161,16 @@ public void addLight(Player player, Location location, int lightLevel) { default -> { } } - player.sendBlockChange(location, light); + lights.add(location); + location.getWorld().setBlockData(location, light); } - public void removeLight(Player player, Location location) { - if (location.getWorld() == null) { - location.setWorld(player.getWorld()); + public void removeLight(Location location) { + Block b = location.getWorld().getBlockAt(location); + if(b.getType() == Material.LIGHT) { + b.setType(Material.AIR); } - player.sendBlockChange(location, location.getWorld().getBlockAt(location).getBlockData()); + lights.remove(location); } public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, @@ -185,19 +211,6 @@ public void removeLastLocation(String uuid) { lastLightLocation.remove(uuid); } - private boolean differentLocations(Location l1, Location l2) { - if (l1 == null || l2 == null) { - return true; - } - if (l1.getWorld() == null || l2.getWorld() == null) { - return true; - } - if (!l1.getWorld().getName().equals(l2.getWorld().getName())) { - return true; - } - return l1.getBlockX() != l2.getBlockX() || l1.getBlockY() != l2.getBlockY() || l1.getBlockZ() != l2.getBlockZ(); - } - private Material getMaterialOrAir(ItemStack item) { if (item == null) return Material.AIR; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1543709..3292746 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,16 +2,17 @@ name: 'DynamicLights' description: 'Emit Light from Held Items using the 1.17 Light Blocks.' website: 'https://www.spigotmc.org/resources/dynamiclights.110707/' author: 'xCykrix' -version: '1.3.0-MULTI-VERSION' +version: '1.3.1' main: github.xCykrix.dynamicLights.DynamicLights api-version: '1.17' load: 'POSTWORLD' +folia-supported: true permissions: dynamiclights.lock: description: "Toggle placing light sources from your off hand." - default: true + default: false dynamiclights.toggle: description: "Toggle rendering of light sources for your client." default: true From e798f645686c7bcc40fa5c5a912e22b47120fd81 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sat, 27 Dec 2025 12:51:16 +0100 Subject: [PATCH 03/35] Switch to paper only & start to update to 1.21.11 --- .gitignore | 3 +++ README.md | 2 +- build.gradle.kts | 22 +++++++++++++++------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 3dadcbe..fc6d893 100644 --- a/.gitignore +++ b/.gitignore @@ -486,3 +486,6 @@ FodyWeavers.xsd # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,java,gradle,kotlin + + +run/ \ No newline at end of file diff --git a/README.md b/README.md index 4f3ce84..84614fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DynamicLights -Emit Light from Held Items using the 1.17 Light Blocks in Spigot/Paper Servers. +Emit Light from Held Items using the 1.17 Light Blocks in Paper Servers. Discord: https://discord.gg/RHJQFPgbke diff --git a/build.gradle.kts b/build.gradle.kts index 1aa8c72..01ef3a0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,10 +4,14 @@ plugins { id("io.github.goooler.shadow") version "8.1.8" id("java") id("java-library") + id("xyz.jpenilla.run-paper") version "2.3.1" } group = "github.xCykrix" -version = "1.3.2" +version = "1.3.3" +description="Dynamic Lights for Minecraft Servers without requiring Modding." +val mainMinecraftVersion = "1.21.11" +val supportedMinecraftVersions = "1.21.11 - 1.21.11" repositories { mavenLocal() @@ -20,16 +24,15 @@ repositories { maven { url = uri("https://maven.pkg.github.com/xCykrix/SpigotDevkit") credentials { - username = project.findProperty("GITHUB_ACTOR").toString() ?: System.getenv("GITHUB_ACTOR") - password = project.findProperty("GITHUB_TOKEN").toString() ?: System.getenv("GITHUB_TOKEN") + username = "" //project.findProperty("GITHUB_ACTOR").toString() ?: System.getenv("GITHUB_ACTOR") + password = "" //project.findProperty("GITHUB_TOKEN").toString() ?: System.getenv("GITHUB_TOKEN") } } } dependencies { - // compileOnly("org.spigotmc:spigot-api:1.21.6-R0.1-SNAPSHOT") - compileOnly("io.papermc.paper:paper-api:1.21.8-R0.1-SNAPSHOT") - implementation("github.xCykrix:spigotdevkit:1.1.0") { + compileOnly("io.papermc.paper:paper-api:$mainMinecraftVersion-R0.1-SNAPSHOT") + implementation("github.xCykrix:spigotdevkit:1.1.1") { isTransitive = false } } @@ -43,9 +46,14 @@ tasks { assemble { dependsOn(shadowJar) } + runServer { + // Configure the Minecraft version for our task. + // This is the only required configuration besides applying the plugin. + // Your plugin's jar (or shadowJar if present) will be used automatically. + minecraftVersion(mainMinecraftVersion) + } } -// Target Java Build (Java 16 - Minecraft 1.17.x) val targetJavaVersion = 21 java { val javaVersion = JavaVersion.toVersion(targetJavaVersion) From bbd96c38ae85da443b7e46a72848f619b9248553 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 13:09:04 +0100 Subject: [PATCH 04/35] Use co.aikar:acf-paper & Reenable light toggle & Remove light-culling-distance config --- .gitignore | 2 +- CHANGELOG.md | 5 ++ build.gradle.kts | 5 +- .../xCykrix/dynamicLights/DynamicLights.java | 12 +-- .../dynamicLights/command/BaseCommand.java | 80 ------------------- .../command/DynamicLightsCommand.java | 70 ++++++++++++++++ .../dynamicLights/event/PlayerHandler.java | 21 +++-- .../dynamicLights/util/LightManager.java | 59 ++++++-------- src/main/resources/config.yml | 3 - 9 files changed, 120 insertions(+), 137 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 src/main/java/github/xCykrix/dynamicLights/command/BaseCommand.java create mode 100644 src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java diff --git a/.gitignore b/.gitignore index fc6d893..bf58377 100644 --- a/.gitignore +++ b/.gitignore @@ -488,4 +488,4 @@ FodyWeavers.xsd # End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,java,gradle,kotlin -run/ \ No newline at end of file +run*/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8bff155 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# 1.3.4 + +Update to 1.21.11 by using co.aikar:acf-paper instead of the custom NMS commands code from github.xCykrix:spigotdevkit. +Reenable light toggle. +Remove light-culling-distance config option as we now use real light that can be seen from any distance. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 01ef3a0..c8064c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.3.3" +version = "1.3.4" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" @@ -32,9 +32,10 @@ repositories { dependencies { compileOnly("io.papermc.paper:paper-api:$mainMinecraftVersion-R0.1-SNAPSHOT") - implementation("github.xCykrix:spigotdevkit:1.1.1") { + implementation("github.xCykrix:spigotdevkit:1.1.2") { isTransitive = false } + implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") } // Shadow Task diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index aaecf8e..46601ba 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -1,7 +1,8 @@ package github.xCykrix.dynamicLights; +import co.aikar.commands.PaperCommandManager; import github.xCykrix.DevkitPlugin; -import github.xCykrix.dynamicLights.command.BaseCommand; +import github.xCykrix.dynamicLights.command.DynamicLightsCommand; import github.xCykrix.dynamicLights.event.PlayerHandler; import github.xCykrix.dynamicLights.util.LightManager; import github.xCykrix.dynamicLights.util.LightSource; @@ -37,10 +38,8 @@ protected void pre() { @Override public void initialize() { // Register Configurations - configuration - .register(new Resource("config.yml", null, this.getResource("config.yml"))) - .register(new Resource("lights.yml", null, this.getResource("lights.yml"))) - .registerLanguageFile(this.getResource("language.yml")); + configuration.register(new Resource("config.yml", null, this.getResource("config.yml"))) + .register(new Resource("lights.yml", null, this.getResource("lights.yml"))).registerLanguageFile(this.getResource("language.yml")); language = configuration.getLanguageFile(); // Register Internal APIs. @@ -53,7 +52,8 @@ public void initialize() { this.getServer().getPluginManager().registerEvents(new PlayerHandler(this), this); // Register Commands - BaseCommand.getOrCreate(this).generate(command); + PaperCommandManager manager = new PaperCommandManager(this); + manager.registerCommand(new DynamicLightsCommand(this)); } @Override diff --git a/src/main/java/github/xCykrix/dynamicLights/command/BaseCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/BaseCommand.java deleted file mode 100644 index c75003b..0000000 --- a/src/main/java/github/xCykrix/dynamicLights/command/BaseCommand.java +++ /dev/null @@ -1,80 +0,0 @@ -package github.xCykrix.dynamicLights.command; - -import dist.xCykrix.shade.dev.jorel.commandapi.CommandAPICommand; -import dist.xCykrix.shade.org.apache.commons.lang3.exception.ExceptionUtils; -import github.xCykrix.DevkitPlugin; -import github.xCykrix.dynamicLights.DynamicLights; -import github.xCykrix.extendable.DevkitSimpleState; -import github.xCykrix.plugin.CommandPlugin; -import java.io.IOException; -import java.util.Objects; - -public class BaseCommand extends DevkitSimpleState { - - public static BaseCommand core; - - public static BaseCommand getOrCreate(DevkitPlugin plugin) { - if (core == null) { - core = new BaseCommand(plugin); - } - return core; - } - - public BaseCommand(DevkitPlugin plugin) { - super(plugin); - } - - public void generate(CommandPlugin commandPlugin) { - new CommandAPICommand("dynamiclights") - .withAliases("dynamiclight", "dl") - .withSubcommand( - new CommandAPICommand("toggle") - .withPermission("dynamiclights.toggle") - .executesPlayer(info -> { - String uuid = info.sender().getUniqueId().toString(); - boolean current = DynamicLights.manager.toggles.getOrDefault(uuid, DynamicLights.manager.toggle); - if (!current) { - DynamicLights.adventure.get().player(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("toggle-on", true)); - DynamicLights.manager.toggles.put(uuid, true); - } else { - DynamicLights.adventure.get().player(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("toggle-off", true)); - DynamicLights.manager.toggles.put(uuid, false); - } - })) - .withSubcommand( - new CommandAPICommand("lock") - .withPermission("dynamiclights.lock") - .executesPlayer(info -> { - String uuid = info.sender().getUniqueId().toString(); - boolean current = DynamicLights.manager.locks.getOrDefault(uuid, true); - if (!current) { - DynamicLights.adventure.get().player(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("enable-lock", true)); - DynamicLights.manager.locks.put(uuid, true); - } else { - DynamicLights.adventure.get().player(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("disable-lock", true)); - DynamicLights.manager.locks.put(uuid, false); - } - })) - .withSubcommand( - new CommandAPICommand("reload") - .withPermission("dynamiclights.reload") - .executes(info -> { - try { - Objects.requireNonNull(DynamicLights.configuration.getYAMLFile("lights.yml")).reload(); - DynamicLights.source.initialize(); - DynamicLights.adventure.get().sender(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("reload", true)); - } catch (IOException | NullPointerException ex) { - DynamicLights.adventure.get().sender(info.sender()).sendMessage( - DynamicLights.language.getComponentFromID("reload-error", true)); - this.plugin.getLogger().severe("Failed to reload lights.yml."); - this.plugin.getLogger().severe(ExceptionUtils.getStackTrace(ex)); - } - })) - .register(this.plugin); - } -} diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java new file mode 100644 index 0000000..0daaa64 --- /dev/null +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -0,0 +1,70 @@ +package github.xCykrix.dynamicLights.command; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import dist.xCykrix.shade.org.apache.commons.lang3.exception.ExceptionUtils; +import github.xCykrix.DevkitPlugin; +import github.xCykrix.dynamicLights.DynamicLights; +import java.io.IOException; +import java.util.Objects; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandAlias("dynamiclights|dynamiclight|dl") +public class DynamicLightsCommand extends co.aikar.commands.BaseCommand {// extends DevkitSimpleState { + + private static DevkitPlugin plugin; + + public DynamicLightsCommand(DevkitPlugin plugin) { this.plugin = plugin; } + + @Subcommand("reload") + @Description("Reloads the plugin config and data files") + @CommandPermission("dynamiclights.reload") + public static void onReload(CommandSender commandSender) { + try { + Objects.requireNonNull(DynamicLights.configuration.getYAMLFile("lights.yml")).reload(); + DynamicLights.source.initialize(); + DynamicLights.adventure.get().sender(commandSender).sendMessage(DynamicLights.language.getComponentFromID("reload", true)); + } catch (IOException | NullPointerException ex) { + DynamicLights.adventure.get().sender(commandSender).sendMessage(DynamicLights.language.getComponentFromID("reload-error", true)); + plugin.getLogger().severe("Failed to reload lights.yml."); + plugin.getLogger().severe(ExceptionUtils.getStackTrace(ex)); + } + } + + @Subcommand("toggle") + @Description("Toggles Dynamic Lights on and off") + @CommandPermission("dynamiclights.toggle") + public static void onToggle(CommandSender commandSender) { + if (commandSender instanceof Player player) { + String uuid = player.getUniqueId().toString(); + boolean current = DynamicLights.manager.toggles.getOrDefault(uuid, DynamicLights.manager.toggle); + if (!current) { + DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("toggle-on", true)); + DynamicLights.manager.toggles.put(uuid, true); + } else { + DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("toggle-off", true)); + DynamicLights.manager.toggles.put(uuid, false); + } + } + } + + @Subcommand("lock") + @Description("Prevents lights from being placed in the offhand") + @CommandPermission("dynamiclights.lock") + public static void onLock(CommandSender commandSender) { + if (commandSender instanceof Player player) { + String uuid = player.getUniqueId().toString(); + boolean current = DynamicLights.manager.locks.getOrDefault(uuid, true); + if (!current) { + DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("enable-lock", true)); + DynamicLights.manager.locks.put(uuid, true); + } else { + DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("disable-lock", true)); + DynamicLights.manager.locks.put(uuid, false); + } + } + } +} diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index f7fbc8e..8c6c350 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -7,14 +7,11 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.EquipmentSlot; public class PlayerHandler extends DevkitSimpleState implements Listener { - public PlayerHandler(DevkitPlugin plugin) { - super(plugin); - } + public PlayerHandler(DevkitPlugin plugin) { super(plugin); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void playerBlockPlaceEvent(BlockPlaceEvent event) { @@ -27,19 +24,19 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { return; } if (DynamicLights.manager.locks.getOrDefault(event.getPlayer().getUniqueId().toString(), DynamicLights.manager.toggle)) { - DynamicLights.adventure.get().player(event.getPlayer()).sendMessage(DynamicLights.language.getComponentFromID("prevent-block-place", true)); + DynamicLights.adventure.get().player(event.getPlayer()) + .sendMessage(DynamicLights.language.getComponentFromID("prevent-block-place", true)); event.setCancelled(true); } } } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerJoinEvent(PlayerJoinEvent event) { - DynamicLights.manager.addPlayer(event.getPlayer()); - } + // No need we do it for each online player + // @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + // public void onPlayerJoinEvent(PlayerJoinEvent event) { + // DynamicLights.manager.addPlayer(event.getPlayer()); + // } @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuitEvent(PlayerQuitEvent event) { - DynamicLights.manager.removePlayer(event.getPlayer().getUniqueId()); - } + public void onPlayerQuitEvent(PlayerQuitEvent event) { DynamicLights.manager.removePlayer(event.getPlayer().getUniqueId()); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 44bb441..18a2b53 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -1,18 +1,18 @@ package github.xCykrix.dynamicLights.util; +import dist.xCykrix.shade.dev.dejvokep.boostedyaml.YamlDocument; +import dist.xCykrix.shade.org.h2.mvstore.MVMap; import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.DynamicLights; import github.xCykrix.extendable.DevkitFullState; import io.papermc.paper.threadedregions.scheduler.ScheduledTask; -import dist.xCykrix.shade.dev.dejvokep.boostedyaml.YamlDocument; -import dist.xCykrix.shade.org.h2.mvstore.MVMap; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; -import java.util.HashSet; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.bukkit.Bukkit; @@ -34,9 +34,8 @@ public class LightManager extends DevkitFullState { public final MVMap toggles; public final MVMap locks; private final long refresh; - private final int distance; public final boolean toggle; - + private final List lights; public LightManager(DevkitPlugin plugin) { @@ -50,37 +49,34 @@ public LightManager(DevkitPlugin plugin) { this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); this.refresh = config.getLong("update-rate"); - this.distance = config.getInt("light-culling-distance"); this.toggle = config.getBoolean("default-toggle-state"); this.lights = Collections.synchronizedList(new LinkedList<>()); - // @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelay, long period, @NotNull TimeUnit unit); + // @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelay, long period, + // @NotNull TimeUnit unit); Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); } @Override - public void initialize() { - } + public void initialize() {} @Override - public void shutdown() { - clearLight(); - } + public void shutdown() { clearLight(); } - public void addPlayer(Player player) {} public void tick() { - if(!plugin.isEnabled()){ + if (!plugin.isEnabled()) { clearLight(); return; } Set actualLocations = new HashSet<>(); + // For each online player, check if we should add a light. for (Player targetPlayer : Bukkit.getOnlinePlayers()) { Optional opLocation = run(targetPlayer); - if(opLocation.isPresent()) { + if (opLocation.isPresent()) { actualLocations.add(opLocation.get()); } } @@ -106,7 +102,10 @@ public void clearLight() { } private Optional run(Player player) { - if(player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR){ + if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { + return Optional.empty(); + } + if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { return Optional.empty(); } @@ -140,11 +139,11 @@ public void addLight(Location location, int lightLevel) { } World world = location.getWorld(); Block block = world.getBlockAt(location); - //Only AIR or LIGHT can be replaced. - if(block.getType() != Material.AIR && block.getType() != Material.LIGHT) { + // Only AIR or LIGHT or WATER can be replaced. + if (block.getType() != Material.AIR && block.getType() != Material.LIGHT && block.getType() != Material.WATER) { return; } - if(block.getBlockData() instanceof Light lightData && lightData.getLevel() == lightLevel) { + if (block.getBlockData() instanceof Light lightData && lightData.getLevel() == lightLevel) { return; } @@ -167,14 +166,14 @@ public void addLight(Location location, int lightLevel) { public void removeLight(Location location) { Block b = location.getWorld().getBlockAt(location); - if(b.getType() == Material.LIGHT) { + if (b.getType() == Material.LIGHT) { b.setType(Material.AIR); } lights.remove(location); } - public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, - Material legging, Material boot) { + public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, + Material boot) { boolean hasLightLevel = false; hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; @@ -190,7 +189,7 @@ public boolean valid(Player player, Material mainHand, Material offHand, Materia if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { return true; } - if (currentLocation instanceof Waterlogged && ((Waterlogged) currentLocation).isWaterlogged()) { + if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { return false; } if (currentLocation.getType() == Material.WATER) { @@ -199,17 +198,11 @@ public boolean valid(Player player, Material mainHand, Material offHand, Materia return false; } - public Location getLastLocation(String uuid) { - return lastLightLocation.getOrDefault(uuid, null); - } + public Location getLastLocation(String uuid) { return lastLightLocation.getOrDefault(uuid, null); } - public void setLastLocation(String uuid, Location location) { - lastLightLocation.put(uuid, location); - } + public void setLastLocation(String uuid, Location location) { lastLightLocation.put(uuid, location); } - public void removeLastLocation(String uuid) { - lastLightLocation.remove(uuid); - } + public void removeLastLocation(String uuid) { lastLightLocation.remove(uuid); } private Material getMaterialOrAir(ItemStack item) { if (item == null) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 642c748..006130f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,9 +1,6 @@ # Update Rate: Controls how often the DynamicLights will tick. Defaults to 5 (4 times per second). Player heavy servers may require less often. update-rate: 5 -# Light Culling Distance: Controls how far away you should see another players DynamicLight. -light-culling-distance: 64 - # Default Toggle State: Controls if Dynamic Lighting is enabled by default. default-toggle-state: true From e67ff8879d43c1ba851654ef12efdddceff786b8 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 13:39:25 +0100 Subject: [PATCH 05/35] start removing dependencies --- build.gradle.kts | 6 +-- .../xCykrix/dynamicLights/DynamicLights.java | 13 +++-- .../command/DynamicLightsCommand.java | 21 ++++++-- .../dynamicLights/event/PlayerHandler.java | 10 ++-- .../dynamicLights/util/LightManager.java | 42 ++++++++------- .../dynamicLights/util/LightSource.java | 53 +++++++++---------- src/main/resources/plugin.yml | 4 +- 7 files changed, 77 insertions(+), 72 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c8064c1..2a35a00 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,9 +32,9 @@ repositories { dependencies { compileOnly("io.papermc.paper:paper-api:$mainMinecraftVersion-R0.1-SNAPSHOT") - implementation("github.xCykrix:spigotdevkit:1.1.2") { - isTransitive = false - } + // implementation("github.xCykrix:spigotdevkit:1.1.2") { + // isTransitive = false + // } implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") } diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index 46601ba..ef14efc 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -1,7 +1,6 @@ package github.xCykrix.dynamicLights; import co.aikar.commands.PaperCommandManager; -import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.command.DynamicLightsCommand; import github.xCykrix.dynamicLights.event.PlayerHandler; import github.xCykrix.dynamicLights.util.LightManager; @@ -10,17 +9,17 @@ import github.xCykrix.plugin.AdventurePlugin; import github.xCykrix.plugin.CommandPlugin; import github.xCykrix.plugin.ConfigurationPlugin; -import github.xCykrix.plugin.H2MVStorePlugin; -import github.xCykrix.records.Resource; +import org.apache.maven.model.Resource; +import org.bukkit.plugin.java.JavaPlugin; -public final class DynamicLights extends DevkitPlugin { +public final class DynamicLights extends JavaPlugin { // Core APIs. public static ConfigurationPlugin configuration; public static AdventurePlugin adventure; public static CommandPlugin command; // Third Party APIs. - public static H2MVStorePlugin h2; + // public static H2MVStorePlugin h2; // Internal APIs. public static LanguageFile language; @@ -32,7 +31,7 @@ protected void pre() { configuration = this.register(new ConfigurationPlugin(this)); adventure = this.register(new AdventurePlugin(this)); command = this.register(new CommandPlugin(this)); - h2 = this.register(new H2MVStorePlugin(this)); + // h2 = this.register(new H2MVStorePlugin(this)); } @Override @@ -56,7 +55,7 @@ public void initialize() { manager.registerCommand(new DynamicLightsCommand(this)); } - @Override + // @Override public void shutdown() { manager.shutdown(); source.shutdown(); diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java index 0daaa64..da500e8 100644 --- a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -4,20 +4,21 @@ import co.aikar.commands.annotation.CommandPermission; import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Subcommand; -import dist.xCykrix.shade.org.apache.commons.lang3.exception.ExceptionUtils; -import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.DynamicLights; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Objects; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; @CommandAlias("dynamiclights|dynamiclight|dl") public class DynamicLightsCommand extends co.aikar.commands.BaseCommand {// extends DevkitSimpleState { - private static DevkitPlugin plugin; + private static JavaPlugin plugin; - public DynamicLightsCommand(DevkitPlugin plugin) { this.plugin = plugin; } + public DynamicLightsCommand(JavaPlugin plugin) { this.plugin = plugin; } @Subcommand("reload") @Description("Reloads the plugin config and data files") @@ -30,7 +31,17 @@ public static void onReload(CommandSender commandSender) { } catch (IOException | NullPointerException ex) { DynamicLights.adventure.get().sender(commandSender).sendMessage(DynamicLights.language.getComponentFromID("reload-error", true)); plugin.getLogger().severe("Failed to reload lights.yml."); - plugin.getLogger().severe(ExceptionUtils.getStackTrace(ex)); + plugin.getLogger().severe(getStackTrace(ex)); + } + } + + public static String getStackTrace(Throwable throwable) { + if (throwable == null) { + return ""; + } else { + StringWriter sw = new StringWriter(); + throwable.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 8c6c350..06e0187 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -1,17 +1,16 @@ package github.xCykrix.dynamicLights.event; -import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.DynamicLights; -import github.xCykrix.extendable.DevkitSimpleState; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.plugin.java.JavaPlugin; -public class PlayerHandler extends DevkitSimpleState implements Listener { - public PlayerHandler(DevkitPlugin plugin) { super(plugin); } +public class PlayerHandler implements Listener { + public PlayerHandler(JavaPlugin plugin) {} @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void playerBlockPlaceEvent(BlockPlaceEvent event) { @@ -24,8 +23,7 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { return; } if (DynamicLights.manager.locks.getOrDefault(event.getPlayer().getUniqueId().toString(), DynamicLights.manager.toggle)) { - DynamicLights.adventure.get().player(event.getPlayer()) - .sendMessage(DynamicLights.language.getComponentFromID("prevent-block-place", true)); + event.getPlayer().sendMessage(DynamicLights.language.getComponentFromID("prevent-block-place", true)); event.setCancelled(true); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 18a2b53..a02c523 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -1,10 +1,6 @@ package github.xCykrix.dynamicLights.util; -import dist.xCykrix.shade.dev.dejvokep.boostedyaml.YamlDocument; -import dist.xCykrix.shade.org.h2.mvstore.MVMap; -import github.xCykrix.DevkitPlugin; import github.xCykrix.dynamicLights.DynamicLights; -import github.xCykrix.extendable.DevkitFullState; import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import java.util.Collections; import java.util.HashMap; @@ -14,6 +10,8 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -25,31 +23,37 @@ import org.bukkit.block.data.type.Light; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; -public class LightManager extends DevkitFullState { +public class LightManager { private final LightSource source; private final HashMap tasks = new HashMap<>(); private final HashMap lastLightLocation = new HashMap<>(); - public final MVMap toggles; - public final MVMap locks; + public final ConcurrentMap toggles; + public final ConcurrentMap locks; private final long refresh; public final boolean toggle; + private JavaPlugin plugin; + private final List lights; - public LightManager(DevkitPlugin plugin) { - super(plugin); - YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); - if (config == null) { - throw new RuntimeException("config.yml is corrupted or contains invalid formatting. Failed to load plugin."); - } + public LightManager(JavaPlugin plugin) { + this.plugin = plugin; + // YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); + // if (config == null) { + // throw new RuntimeException("config.yml is corrupted or contains invalid formatting. Failed to load plugin."); + // } this.source = DynamicLights.source; - this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); - this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); - this.refresh = config.getLong("update-rate"); - this.toggle = config.getBoolean("default-toggle-state"); + // TODO reenable file storing of toggles & locks + // this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); + // this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); + this.toggles = new ConcurrentHashMap<>(); + this.locks = new ConcurrentHashMap<>(); + this.refresh = plugin.getConfig().getLong("update-rate"); + this.toggle = plugin.getConfig().getBoolean("default-toggle-state"); this.lights = Collections.synchronizedList(new LinkedList<>()); @@ -58,10 +62,8 @@ public LightManager(DevkitPlugin plugin) { Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); } - @Override - public void initialize() {} - @Override + // @Override public void shutdown() { clearLight(); } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java b/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java index fe444f6..05cc208 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java @@ -1,41 +1,45 @@ package github.xCykrix.dynamicLights.util; -import github.xCykrix.DevkitPlugin; -import github.xCykrix.dynamicLights.DynamicLights; import github.xCykrix.extendable.DevkitFullState; -import dist.xCykrix.shade.dev.dejvokep.boostedyaml.YamlDocument; -import dist.xCykrix.shade.dev.dejvokep.boostedyaml.block.Block; +import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; public class LightSource extends DevkitFullState { private final HashMap levelOfLights = new HashMap<>(); private final HashSet submersibleLights = new HashSet<>(); private final HashSet lockedLights = new HashSet<>(); - public LightSource(DevkitPlugin plugin) { - super(plugin); - } + public LightSource(JavaPlugin plugin) { super(plugin); } @Override public void initialize() { - YamlDocument lights = DynamicLights.configuration.getYAMLFile("lights.yml"); + // YamlDocument lights = DynamicLights.configuration.getYAMLFile("lights.yml"); + YamlConfiguration lights = YamlConfiguration.loadConfiguration(new File(this.plugin.getDataFolder(), "lights.yml")); + + if (lights == null) { throw new RuntimeException("lights.yml is corrupted or contains invalid formatting. Failed to load plugin."); } // Register Light Levels this.levelOfLights.clear(); - Map> levels = lights.getSection("levels").getStoredValue(); - for (Object material : levels.keySet()) { + // Map> levels = lights.getSection("levels").getStoredValue(); + Map levels = lights.getConfigurationSection("levels").getValues(false); + for (Map.Entry entry : levels.entrySet()) { + String materialString = entry.getKey(); + Object levelObject = entry.getValue(); try { - int level = Integer.parseInt(levels.get(material).getStoredValue().toString()); - this.levelOfLights.put(Material.valueOf((String) material), level); + int level = Integer.parseInt(levelObject.toString()); + Material material = Material.valueOf(materialString); + this.levelOfLights.put(material, level); } catch (Exception exception) { - this.plugin.getLogger().warning("Unable to register level for '" + material + "'. " + exception.getMessage()); + this.plugin.getLogger().warning("Unable to register level for '" + materialString + "'. " + exception.getMessage()); } } this.plugin.getLogger().info("Registered " + this.levelOfLights.size() + " items for Dynamic Lights."); @@ -47,12 +51,10 @@ public void initialize() { try { this.submersibleLights.add(Material.valueOf(material)); } catch (Exception exception) { - this.plugin.getLogger() - .warning("Unable to register submersible for '" + material + "'. " + exception.getMessage()); + this.plugin.getLogger().warning("Unable to register submersible for '" + material + "'. " + exception.getMessage()); } } - this.plugin.getLogger() - .info("Registered " + this.submersibleLights.size() + " items for Dynamic Submersible Lights."); + this.plugin.getLogger().info("Registered " + this.submersibleLights.size() + " items for Dynamic Submersible Lights."); // Register Lockable Lights this.lockedLights.clear(); @@ -61,8 +63,7 @@ public void initialize() { try { this.lockedLights.add(Material.valueOf(material)); } catch (Exception exception) { - this.plugin.getLogger() - .warning("Unable to register lockable for '" + material + "'. " + exception.getMessage()); + this.plugin.getLogger().warning("Unable to register lockable for '" + material + "'. " + exception.getMessage()); } } this.plugin.getLogger().info("Registered " + this.lockedLights.size() + " items for Dynamic Locked Lights."); @@ -75,12 +76,9 @@ public void shutdown() { this.levelOfLights.clear(); } - public boolean hasLightLevel(Material material) { - return levelOfLights.containsKey(material); - } + public boolean hasLightLevel(Material material) { return levelOfLights.containsKey(material); } - public Integer getLightLevel(Material mainHand, Material offHand, Material helmet, Material chestplate, - Material legging, Material boot) { + public Integer getLightLevel(Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, Material boot) { int level = 0; level = levelOfLights.getOrDefault(boot, level); level = levelOfLights.getOrDefault(legging, level); @@ -91,8 +89,7 @@ public Integer getLightLevel(Material mainHand, Material offHand, Material helme return level; } - public boolean isSubmersible(Material mainHand, Material offHand, Material helmet, Material chestplate, - Material legging, Material boot) { + public boolean isSubmersible(Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, Material boot) { boolean submersible = false; submersible = submersibleLights.contains(boot) ? true : submersible; submersible = submersibleLights.contains(legging) ? true : submersible; @@ -103,7 +100,5 @@ public boolean isSubmersible(Material mainHand, Material offHand, Material helme return submersible; } - public boolean isProtectedLight(Material offHand) { - return lockedLights.contains(offHand); - } + public boolean isProtectedLight(Material offHand) { return lockedLights.contains(offHand); } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3292746..d3ee7f6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,11 +1,11 @@ name: 'DynamicLights' -description: 'Emit Light from Held Items using the 1.17 Light Blocks.' +description: 'Emit Light from Held Items using the 1.20 Light Blocks.' website: 'https://www.spigotmc.org/resources/dynamiclights.110707/' author: 'xCykrix' version: '1.3.1' main: github.xCykrix.dynamicLights.DynamicLights -api-version: '1.17' +api-version: '1.20' load: 'POSTWORLD' folia-supported: true From aa06bf66ae09ee273deb5ef59ef9006ac43e2b22 Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 14:04:58 +0100 Subject: [PATCH 06/35] Tests other possible location for the player light to avoid bug in dors or other partialy empty block --- .../dynamicLights/util/LightManager.java | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 18a2b53..5917a29 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -21,6 +21,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.Light; import org.bukkit.entity.Player; @@ -133,22 +134,46 @@ public void removePlayer(UUID uid) { } } + private boolean acceptableBlock(Block block) { + Material type = block.getType(); + if (type == Material.AIR || type == Material.CAVE_AIR || type == Material.WATER) { + return true; + } + if (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { + return true; + } + return false; + } + + private boolean getClosestAcceptableBlock(Block block) { + List possibleLocation = List.of(block,block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), block.getRelative(BlockFace.SOUTH), + block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), block.getRelative(BlockFace.DOWN)); + + for (Block relativeBlock : possibleLocation) { + if (acceptableBlock(relativeBlock)) { + return relativeBlock; + } + } + return null; + } + public void addLight(Location location, int lightLevel) { if (lightLevel == 0) { return; } World world = location.getWorld(); Block block = world.getBlockAt(location); + + + // Only AIR or LIGHT or WATER can be replaced. - if (block.getType() != Material.AIR && block.getType() != Material.LIGHT && block.getType() != Material.WATER) { - return; - } - if (block.getBlockData() instanceof Light lightData && lightData.getLevel() == lightLevel) { - return; + block = getClosestAcceptableBlock(block); + if(!block){ + return; } - + Light light = (Light) Material.LIGHT.createBlockData(); - switch (world.getBlockAt(location).getType()) { + switch (block.getType()) { case AIR, CAVE_AIR -> { light.setWaterlogged(false); light.setLevel(lightLevel); From deaeac4f4113c4d85015362db3489eeee61a11d6 Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 14:07:15 +0100 Subject: [PATCH 07/35] corecting a bug with getClosestAcceptableBlock --- .../github/xCykrix/dynamicLights/util/LightManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 5917a29..85cc9e1 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -145,7 +145,7 @@ private boolean acceptableBlock(Block block) { return false; } - private boolean getClosestAcceptableBlock(Block block) { + private Block getClosestAcceptableBlock(Block block) { List possibleLocation = List.of(block,block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), block.getRelative(BlockFace.SOUTH), block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), block.getRelative(BlockFace.DOWN)); @@ -168,10 +168,10 @@ public void addLight(Location location, int lightLevel) { // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); - if(!block){ + if(block == null){ return; } - + Light light = (Light) Material.LIGHT.createBlockData(); switch (block.getType()) { case AIR, CAVE_AIR -> { From 9753c36a1904a8841b2f6bd82c45db805a509ca8 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 14:12:39 +0100 Subject: [PATCH 08/35] Remove the dependency --- .../xCykrix/dynamicLights/DynamicLights.java | 36 +++++++++---------- .../command/DynamicLightsCommand.java | 21 +++++------ .../dynamicLights/event/PlayerHandler.java | 2 +- .../dynamicLights/util/LightSource.java | 11 +++--- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index ef14efc..b7f261c 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -5,47 +5,42 @@ import github.xCykrix.dynamicLights.event.PlayerHandler; import github.xCykrix.dynamicLights.util.LightManager; import github.xCykrix.dynamicLights.util.LightSource; -import github.xCykrix.helper.LanguageFile; -import github.xCykrix.plugin.AdventurePlugin; -import github.xCykrix.plugin.CommandPlugin; -import github.xCykrix.plugin.ConfigurationPlugin; -import org.apache.maven.model.Resource; import org.bukkit.plugin.java.JavaPlugin; public final class DynamicLights extends JavaPlugin { // Core APIs. - public static ConfigurationPlugin configuration; - public static AdventurePlugin adventure; - public static CommandPlugin command; + // public static ConfigurationPlugin configuration; + // public static AdventurePlugin adventure; + // public static CommandPlugin command; // Third Party APIs. // public static H2MVStorePlugin h2; // Internal APIs. - public static LanguageFile language; + // public static LanguageFile language; public static LightSource source; public static LightManager manager; - @Override + // @Override protected void pre() { - configuration = this.register(new ConfigurationPlugin(this)); - adventure = this.register(new AdventurePlugin(this)); - command = this.register(new CommandPlugin(this)); + // configuration = this.register(new ConfigurationPlugin(this)); + // adventure = this.register(new AdventurePlugin(this)); + // command = this.register(new CommandPlugin(this)); // h2 = this.register(new H2MVStorePlugin(this)); } - @Override + // @Override public void initialize() { // Register Configurations - configuration.register(new Resource("config.yml", null, this.getResource("config.yml"))) - .register(new Resource("lights.yml", null, this.getResource("lights.yml"))).registerLanguageFile(this.getResource("language.yml")); - language = configuration.getLanguageFile(); + // configuration.register(new Resource("config.yml", null, this.getResource("config.yml"))) + // .register(new Resource("lights.yml", null, this.getResource("lights.yml"))).registerLanguageFile(this.getResource("language.yml")); + // language = configuration.getLanguageFile(); // Register Internal APIs. source = new LightSource(this); source.initialize(); manager = new LightManager(this); - manager.initialize(); + // manager.initialize(); // Register Events this.getServer().getPluginManager().registerEvents(new PlayerHandler(this), this); @@ -60,4 +55,9 @@ public void shutdown() { manager.shutdown(); source.shutdown(); } + + public static String translate(String key) { + // TODO fix language file access & color interpretation + return key; + } } diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java index da500e8..776a4f9 100644 --- a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -5,11 +5,11 @@ import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Subcommand; import github.xCykrix.dynamicLights.DynamicLights; -import java.io.IOException; +import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.Objects; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; @@ -25,11 +25,12 @@ public class DynamicLightsCommand extends co.aikar.commands.BaseCommand {// exte @CommandPermission("dynamiclights.reload") public static void onReload(CommandSender commandSender) { try { - Objects.requireNonNull(DynamicLights.configuration.getYAMLFile("lights.yml")).reload(); + // Objects.requireNonNull(DynamicLights.configuration.getYAMLFile("lights.yml")).reload(); + YamlConfiguration lights = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "lights.yml")); DynamicLights.source.initialize(); - DynamicLights.adventure.get().sender(commandSender).sendMessage(DynamicLights.language.getComponentFromID("reload", true)); - } catch (IOException | NullPointerException ex) { - DynamicLights.adventure.get().sender(commandSender).sendMessage(DynamicLights.language.getComponentFromID("reload-error", true)); + commandSender.sendMessage(DynamicLights.translate("reload")); + } catch (NullPointerException ex) { + commandSender.sendMessage(DynamicLights.translate("reload-error")); plugin.getLogger().severe("Failed to reload lights.yml."); plugin.getLogger().severe(getStackTrace(ex)); } @@ -53,10 +54,10 @@ public static void onToggle(CommandSender commandSender) { String uuid = player.getUniqueId().toString(); boolean current = DynamicLights.manager.toggles.getOrDefault(uuid, DynamicLights.manager.toggle); if (!current) { - DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("toggle-on", true)); + player.sendMessage(DynamicLights.translate("toggle-on")); DynamicLights.manager.toggles.put(uuid, true); } else { - DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("toggle-off", true)); + player.sendMessage(DynamicLights.translate("toggle-off")); DynamicLights.manager.toggles.put(uuid, false); } } @@ -70,10 +71,10 @@ public static void onLock(CommandSender commandSender) { String uuid = player.getUniqueId().toString(); boolean current = DynamicLights.manager.locks.getOrDefault(uuid, true); if (!current) { - DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("enable-lock", true)); + player.sendMessage(DynamicLights.translate("enable-lock")); DynamicLights.manager.locks.put(uuid, true); } else { - DynamicLights.adventure.get().player(player).sendMessage(DynamicLights.language.getComponentFromID("disable-lock", true)); + player.sendMessage(DynamicLights.translate("disable-lock")); DynamicLights.manager.locks.put(uuid, false); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 06e0187..38fd616 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -23,7 +23,7 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { return; } if (DynamicLights.manager.locks.getOrDefault(event.getPlayer().getUniqueId().toString(), DynamicLights.manager.toggle)) { - event.getPlayer().sendMessage(DynamicLights.language.getComponentFromID("prevent-block-place", true)); + event.getPlayer().sendMessage(DynamicLights.translate("prevent-block-place")); event.setCancelled(true); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java b/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java index 05cc208..2faef80 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightSource.java @@ -1,6 +1,5 @@ package github.xCykrix.dynamicLights.util; -import github.xCykrix.extendable.DevkitFullState; import java.io.File; import java.util.HashMap; import java.util.HashSet; @@ -10,14 +9,16 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; -public class LightSource extends DevkitFullState { +public class LightSource { private final HashMap levelOfLights = new HashMap<>(); private final HashSet submersibleLights = new HashSet<>(); private final HashSet lockedLights = new HashSet<>(); - public LightSource(JavaPlugin plugin) { super(plugin); } + private final JavaPlugin plugin; - @Override + public LightSource(JavaPlugin plugin) { this.plugin = plugin; } + + // @Override public void initialize() { // YamlDocument lights = DynamicLights.configuration.getYAMLFile("lights.yml"); YamlConfiguration lights = YamlConfiguration.loadConfiguration(new File(this.plugin.getDataFolder(), "lights.yml")); @@ -69,7 +70,7 @@ public void initialize() { this.plugin.getLogger().info("Registered " + this.lockedLights.size() + " items for Dynamic Locked Lights."); } - @Override + // @Override public void shutdown() { this.lockedLights.clear(); this.submersibleLights.clear(); From afdae634bf3d35944f0397f5cdaecc75fb28e4a9 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 14:28:13 +0100 Subject: [PATCH 09/35] Save default config stuff & enable the plugin. --- .../github/xCykrix/dynamicLights/DynamicLights.java | 10 ++++++++++ .../xCykrix/dynamicLights/util/LightManager.java | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index b7f261c..8bf49c0 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -31,6 +31,13 @@ protected void pre() { // @Override public void initialize() { + + // save default config files + saveDefaultConfig(); + saveResource("language.yml", false); + saveResource("lights.yml", false); + + // Register Configurations // configuration.register(new Resource("config.yml", null, this.getResource("config.yml"))) // .register(new Resource("lights.yml", null, this.getResource("lights.yml"))).registerLanguageFile(this.getResource("language.yml")); @@ -50,6 +57,9 @@ public void initialize() { manager.registerCommand(new DynamicLightsCommand(this)); } + @Override + public void onEnable() { initialize(); } + // @Override public void shutdown() { manager.shutdown(); diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index a02c523..fba3cc4 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -73,10 +73,13 @@ public void tick() { return; } + plugin.getLogger().info("tick with enabled plugin"); + Set actualLocations = new HashSet<>(); // For each online player, check if we should add a light. for (Player targetPlayer : Bukkit.getOnlinePlayers()) { + plugin.getLogger().info("player " + targetPlayer.getName() + " is online"); Optional opLocation = run(targetPlayer); if (opLocation.isPresent()) { actualLocations.add(opLocation.get()); @@ -105,9 +108,11 @@ public void clearLight() { private Optional run(Player player) { if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { + plugin.getLogger().info("creative or spectator"); return Optional.empty(); } if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { + plugin.getLogger().info("toggle false"); return Optional.empty(); } @@ -119,10 +124,12 @@ private Optional run(Player player) { Material boot = getMaterialOrAir(player.getInventory().getBoots()); int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); if (lightLevel > 0) { + plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(player.getEyeLocation(), lightLevel)); return Optional.of(eyeLocation); } + plugin.getLogger().info("lightLevel <= 0"); return Optional.empty(); } From 05edb8203437b954b7540d37a7311769cc8d192b Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 14:29:47 +0100 Subject: [PATCH 10/35] disable debug log --- .../xCykrix/dynamicLights/util/LightManager.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index fba3cc4..934e831 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -73,13 +73,13 @@ public void tick() { return; } - plugin.getLogger().info("tick with enabled plugin"); + // plugin.getLogger().info("tick with enabled plugin"); Set actualLocations = new HashSet<>(); // For each online player, check if we should add a light. for (Player targetPlayer : Bukkit.getOnlinePlayers()) { - plugin.getLogger().info("player " + targetPlayer.getName() + " is online"); + // plugin.getLogger().info("player " + targetPlayer.getName() + " is online"); Optional opLocation = run(targetPlayer); if (opLocation.isPresent()) { actualLocations.add(opLocation.get()); @@ -108,11 +108,11 @@ public void clearLight() { private Optional run(Player player) { if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { - plugin.getLogger().info("creative or spectator"); + // plugin.getLogger().info("creative or spectator"); return Optional.empty(); } if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { - plugin.getLogger().info("toggle false"); + // plugin.getLogger().info("toggle false");plugin.getLogger().info return Optional.empty(); } @@ -124,12 +124,12 @@ private Optional run(Player player) { Material boot = getMaterialOrAir(player.getInventory().getBoots()); int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); if (lightLevel > 0) { - plugin.getLogger().info("lightLevel > 0 " + lightLevel); + // plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(player.getEyeLocation(), lightLevel)); return Optional.of(eyeLocation); } - plugin.getLogger().info("lightLevel <= 0"); + // plugin.getLogger().info("lightLevel <= 0"); return Optional.empty(); } From 7016af4acb973b69312a1b572f8757b028085e27 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sun, 28 Dec 2025 14:44:19 +0100 Subject: [PATCH 11/35] Add LIGHT back in acceptableBlock --- .../dynamicLights/util/LightManager.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 3a0df6a..8753dfb 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -145,23 +145,18 @@ public void removePlayer(UUID uid) { private boolean acceptableBlock(Block block) { Material type = block.getType(); - if (type == Material.AIR || type == Material.CAVE_AIR || type == Material.WATER) { - return true; - } - if (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { - return true; - } - return false; + return type == Material.AIR || type == Material.CAVE_AIR || type == Material.WATER || type == Material.LIGHT; } private Block getClosestAcceptableBlock(Block block) { - List possibleLocation = List.of(block,block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), block.getRelative(BlockFace.SOUTH), - block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), block.getRelative(BlockFace.DOWN)); + List possibleLocation = List.of(block, block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), + block.getRelative(BlockFace.SOUTH), block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), + block.getRelative(BlockFace.DOWN)); for (Block relativeBlock : possibleLocation) { - if (acceptableBlock(relativeBlock)) { - return relativeBlock; - } + if (acceptableBlock(relativeBlock)) { + return relativeBlock; + } } return null; } @@ -173,12 +168,11 @@ public void addLight(Location location, int lightLevel) { World world = location.getWorld(); Block block = world.getBlockAt(location); - // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); - if(block == null){ - return; + if (block == null) { + return; } Light light = (Light) Material.LIGHT.createBlockData(); From 83399afcdd1f0dac6d4e7212b76a78e1ca4922cc Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 15:12:34 +0100 Subject: [PATCH 12/35] correction to avoir breaking block --- .../github/xCykrix/dynamicLights/util/LightManager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 8753dfb..56f3e01 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -127,7 +127,7 @@ private Optional run(Player player) { if (lightLevel > 0) { // plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); - Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(player.getEyeLocation(), lightLevel)); + Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(eyeLocation, lightLevel)); return Optional.of(eyeLocation); } // plugin.getLogger().info("lightLevel <= 0"); @@ -155,6 +155,7 @@ private Block getClosestAcceptableBlock(Block block) { for (Block relativeBlock : possibleLocation) { if (acceptableBlock(relativeBlock)) { + // if(relativeBlock.getType() != Material.LIGHT) return relativeBlock; } } @@ -171,10 +172,13 @@ public void addLight(Location location, int lightLevel) { // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); + if (block == null) { return; } + location = block.getLocation(); + Light light = (Light) Material.LIGHT.createBlockData(); switch (block.getType()) { case AIR, CAVE_AIR -> { From d82f818dbd5b19cfb42f5fee5019e3a8c5f5bac9 Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 15:25:51 +0100 Subject: [PATCH 13/35] working light (but not under water) --- .../dynamicLights/util/LightManager.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 56f3e01..2e2f4b9 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -145,7 +145,9 @@ public void removePlayer(UUID uid) { private boolean acceptableBlock(Block block) { Material type = block.getType(); - return type == Material.AIR || type == Material.CAVE_AIR || type == Material.WATER || type == Material.LIGHT; + return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT; + // TODO reenable water lights + // || type == Material.WATER; } private Block getClosestAcceptableBlock(Block block) { @@ -155,7 +157,6 @@ private Block getClosestAcceptableBlock(Block block) { for (Block relativeBlock : possibleLocation) { if (acceptableBlock(relativeBlock)) { - // if(relativeBlock.getType() != Material.LIGHT) return relativeBlock; } } @@ -166,16 +167,28 @@ public void addLight(Location location, int lightLevel) { if (lightLevel == 0) { return; } + + World world = location.getWorld(); Block block = world.getBlockAt(location); - + // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); if (block == null) { - return; - } + return; + } + + if (block.getType() == Material.LIGHT) { + // return if existing light level if the same + Light existingLight = (Light) block.getBlockData(); + if (existingLight.getLevel() == lightLevel) { + return; + } + } + + location = block.getLocation(); From 640a0c1db764c02a73ba369db913bed1cd0cbafa Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 15:34:42 +0100 Subject: [PATCH 14/35] also working under water --- .../xCykrix/dynamicLights/util/LightManager.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 2e2f4b9..9bf5e4e 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -145,7 +145,7 @@ public void removePlayer(UUID uid) { private boolean acceptableBlock(Block block) { Material type = block.getType(); - return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT; + return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; // TODO reenable water lights // || type == Material.WATER; } @@ -175,7 +175,7 @@ public void addLight(Location location, int lightLevel) { // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); - + if (block == null) { return; } @@ -212,7 +212,13 @@ public void addLight(Location location, int lightLevel) { public void removeLight(Location location) { Block b = location.getWorld().getBlockAt(location); if (b.getType() == Material.LIGHT) { - b.setType(Material.AIR); + if (b.getBlockData() instanceof Light light) { + if (light.isWaterlogged()) { + b.setType(Material.WATER); + }else{ + b.setType(Material.AIR); + } + } } lights.remove(location); } From 435a5b3e0fd266db5763dde3a17fd5922726ac4f Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 15:45:39 +0100 Subject: [PATCH 15/35] using submersible variable removing unused function valid() --- .../dynamicLights/util/LightManager.java | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 9bf5e4e..3bdf020 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -127,7 +127,7 @@ private Optional run(Player player) { if (lightLevel > 0) { // plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); - Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(eyeLocation, lightLevel)); + Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(eyeLocation, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot))); return Optional.of(eyeLocation); } // plugin.getLogger().info("lightLevel <= 0"); @@ -146,8 +146,6 @@ public void removePlayer(UUID uid) { private boolean acceptableBlock(Block block) { Material type = block.getType(); return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; - // TODO reenable water lights - // || type == Material.WATER; } private Block getClosestAcceptableBlock(Block block) { @@ -163,7 +161,7 @@ private Block getClosestAcceptableBlock(Block block) { return null; } - public void addLight(Location location, int lightLevel) { + public void addLight(Location location, int lightLevel, boolean isSubmersible) { if (lightLevel == 0) { return; } @@ -199,8 +197,12 @@ public void addLight(Location location, int lightLevel) { light.setLevel(lightLevel); } case WATER -> { - light.setWaterlogged(true); - light.setLevel(lightLevel); + if(isSubmersible){ + light.setWaterlogged(true); + light.setLevel(lightLevel); + }else{ + return; + } } default -> { } @@ -223,31 +225,31 @@ public void removeLight(Location location) { lights.remove(location); } - public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, - Material boot) { - boolean hasLightLevel = false; - hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; - hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; - hasLightLevel = source.hasLightLevel(helmet) ? true : hasLightLevel; - hasLightLevel = source.hasLightLevel(chestplate) ? true : hasLightLevel; - hasLightLevel = source.hasLightLevel(legging) ? true : hasLightLevel; - hasLightLevel = source.hasLightLevel(boot) ? true : hasLightLevel; - - if (!hasLightLevel) { - return false; - } - Block currentLocation = player.getEyeLocation().getBlock(); - if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { - return true; - } - if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { - return false; - } - if (currentLocation.getType() == Material.WATER) { - return source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot); - } - return false; - } + // public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, + // Material boot) { + // boolean hasLightLevel = false; + // hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(helmet) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(chestplate) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(legging) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(boot) ? true : hasLightLevel; + + // if (!hasLightLevel) { + // return false; + // } + // Block currentLocation = player.getEyeLocation().getBlock(); + // if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { + // return true; + // } + // if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { + // return false; + // } + // if (currentLocation.getType() == Material.WATER) { + // return source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot); + // } + // return false; + // } public Location getLastLocation(String uuid) { return lastLightLocation.getOrDefault(uuid, null); } From d449f508395e5c4ddb6d5fa9118f7bfd359ecbd3 Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 15:50:21 +0100 Subject: [PATCH 16/35] add jack o lantern --- src/main/resources/lights.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/lights.yml b/src/main/resources/lights.yml index 93eb178..9a7ae7c 100644 --- a/src/main/resources/lights.yml +++ b/src/main/resources/lights.yml @@ -15,6 +15,7 @@ levels: OCHRE_FROGLIGHT: 15 PEARLESCENT_FROGLIGHT: 15 VERDANT_FROGLIGHT: 15 + JACK_O_LANTERN: 15 # String List of Submersible Materials submersibles: @@ -24,6 +25,9 @@ submersibles: - "OCHRE_FROGLIGHT" - "PEARLESCENT_FROGLIGHT" - "VERDANT_FROGLIGHT" + - "JACK_O_LANTERN" + + # String List of Lockable Materials lockables: From e6ea23ad2e21a845623e4461c14f5af0bf5b2474 Mon Sep 17 00:00:00 2001 From: Eugene-74 Date: Sun, 28 Dec 2025 16:02:26 +0100 Subject: [PATCH 17/35] new version --- build.gradle.kts | 2 +- .../java/github/xCykrix/dynamicLights/util/LightManager.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2a35a00..68f6fbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.3.4" +version = "1.4.0" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 3bdf020..3f7a9e0 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -183,7 +183,9 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { Light existingLight = (Light) block.getBlockData(); if (existingLight.getLevel() == lightLevel) { return; + } + } From 5c05a9fbdc39378a72e22dcc6381fe0ba7fa9107 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Fri, 2 Jan 2026 16:00:55 +0100 Subject: [PATCH 18/35] Add a 2 tick delay to avoid light being removed before the new one is added. This is probably because we have no garanty about UDP packet being recived in order or region sheduler running task in order. --- build.gradle.kts | 15 ++- .../dynamicLights/util/LightManager.java | 101 +++++++++--------- src/main/resources/plugin.yml | 10 +- 3 files changed, 69 insertions(+), 57 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 68f6fbc..841745a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.0" +version = "1.4.1" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" @@ -47,6 +47,19 @@ tasks { assemble { dependsOn(shadowJar) } + processResources { + val props = mapOf( + "name" to project.name, + "version" to project.version, + "description" to project.description, + "apiVersion" to "1.21.11", + "group" to project.group + ) + inputs.properties(props) + filesMatching("plugin.yml") { + expand(props) + } + } runServer { // Configure the Minecraft version for our task. // This is the only required configuration besides applying the plugin. diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 3f7a9e0..15f6a8c 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -20,7 +20,6 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.Light; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -91,7 +90,9 @@ public void tick() { synchronized (this.lights) { for (Location location : this.lights) { if (!actualLocations.contains(location)) { - Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); + // Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); + // 2 tick delay to avoid packet about removing light being received faster than the one adding the new light. + Bukkit.getRegionScheduler().runDelayed(plugin, location, st -> this.removeLight(location), 2); } } this.lights.clear(); @@ -127,7 +128,8 @@ private Optional run(Player player) { if (lightLevel > 0) { // plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); - Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(eyeLocation, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot))); + Bukkit.getRegionScheduler().run(plugin, eyeLocation, + st -> this.addLight(eyeLocation, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot))); return Optional.of(eyeLocation); } // plugin.getLogger().info("lightLevel <= 0"); @@ -145,7 +147,7 @@ public void removePlayer(UUID uid) { private boolean acceptableBlock(Block block) { Material type = block.getType(); - return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; + return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; } private Block getClosestAcceptableBlock(Block block) { @@ -170,25 +172,24 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { World world = location.getWorld(); Block block = world.getBlockAt(location); - + // Only AIR or LIGHT or WATER can be replaced. block = getClosestAcceptableBlock(block); if (block == null) { - return; - } + return; + } if (block.getType() == Material.LIGHT) { - // return if existing light level if the same - Light existingLight = (Light) block.getBlockData(); - if (existingLight.getLevel() == lightLevel) { - return; + // return if existing light level if the same + Light existingLight = (Light) block.getBlockData(); + if (existingLight.getLevel() == lightLevel) { + return; - } - } - - + + } + location = block.getLocation(); @@ -199,12 +200,12 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { light.setLevel(lightLevel); } case WATER -> { - if(isSubmersible){ - light.setWaterlogged(true); - light.setLevel(lightLevel); - }else{ - return; - } + if (isSubmersible) { + light.setWaterlogged(true); + light.setLevel(lightLevel); + } else { + return; + } } default -> { } @@ -215,42 +216,40 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { public void removeLight(Location location) { Block b = location.getWorld().getBlockAt(location); - if (b.getType() == Material.LIGHT) { - if (b.getBlockData() instanceof Light light) { - if (light.isWaterlogged()) { - b.setType(Material.WATER); - }else{ - b.setType(Material.AIR); - } + if (b.getBlockData() instanceof Light light) { + if (light.isWaterlogged()) { + b.setType(Material.WATER); + } else { + b.setType(Material.AIR); } } lights.remove(location); } // public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, - // Material boot) { - // boolean hasLightLevel = false; - // hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(helmet) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(chestplate) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(legging) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(boot) ? true : hasLightLevel; - - // if (!hasLightLevel) { - // return false; - // } - // Block currentLocation = player.getEyeLocation().getBlock(); - // if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { - // return true; - // } - // if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { - // return false; - // } - // if (currentLocation.getType() == Material.WATER) { - // return source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot); - // } - // return false; + // Material boot) { + // boolean hasLightLevel = false; + // hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(helmet) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(chestplate) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(legging) ? true : hasLightLevel; + // hasLightLevel = source.hasLightLevel(boot) ? true : hasLightLevel; + + // if (!hasLightLevel) { + // return false; + // } + // Block currentLocation = player.getEyeLocation().getBlock(); + // if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { + // return true; + // } + // if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { + // return false; + // } + // if (currentLocation.getType() == Material.WATER) { + // return source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot); + // } + // return false; // } public Location getLastLocation(String uuid) { return lastLightLocation.getOrDefault(uuid, null); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d3ee7f6..6da9f99 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,11 +1,11 @@ -name: 'DynamicLights' +name: '$name' description: 'Emit Light from Held Items using the 1.20 Light Blocks.' -website: 'https://www.spigotmc.org/resources/dynamiclights.110707/' -author: 'xCykrix' -version: '1.3.1' +website: 'https://github.com/Mvndi/DynamicLights' +authors: [ 'xCykrix', 'Hydrolien' ] +version: $version main: github.xCykrix.dynamicLights.DynamicLights -api-version: '1.20' +api-version: "$apiVersion" load: 'POSTWORLD' folia-supported: true From 736e876801681d91e92f5a10b8b7f503fcb52ad9 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Fri, 13 Feb 2026 12:27:37 +0100 Subject: [PATCH 19/35] Fix light location being compare between block & player eye wich was never the same making the light flicker. --- CHANGELOG.md | 4 ++++ build.gradle.kts | 2 +- .../github/xCykrix/dynamicLights/DynamicLights.java | 2 ++ .../xCykrix/dynamicLights/util/LightManager.java | 12 +++++++----- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bff155..c1d9dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.4.2 + +Fix light location being compare between block & player eye wich was never the same making the light flicker. + # 1.3.4 Update to 1.21.11 by using co.aikar:acf-paper instead of the custom NMS commands code from github.xCykrix:spigotdevkit. diff --git a/build.gradle.kts b/build.gradle.kts index 841745a..29b208f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.1" +version = "1.4.2" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index 8bf49c0..1eafe9b 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -70,4 +70,6 @@ public static String translate(String key) { // TODO fix language file access & color interpretation return key; } + + public static DynamicLights getInstance() { return DynamicLights.getPlugin(DynamicLights.class); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 15f6a8c..07216cf 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -90,9 +90,7 @@ public void tick() { synchronized (this.lights) { for (Location location : this.lights) { if (!actualLocations.contains(location)) { - // Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); - // 2 tick delay to avoid packet about removing light being received faster than the one adding the new light. - Bukkit.getRegionScheduler().runDelayed(plugin, location, st -> this.removeLight(location), 2); + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); } } this.lights.clear(); @@ -105,6 +103,7 @@ public void clearLight() { for (Location location : lights) { Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); } + this.lights.clear(); } } @@ -130,7 +129,8 @@ private Optional run(Player player) { Location eyeLocation = player.getEyeLocation(); Bukkit.getRegionScheduler().run(plugin, eyeLocation, st -> this.addLight(eyeLocation, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot))); - return Optional.of(eyeLocation); + Location blockEyeLocation = getClosestAcceptableBlock(eyeLocation.getBlock()).getLocation(); + return Optional.of(blockEyeLocation); } // plugin.getLogger().info("lightLevel <= 0"); return Optional.empty(); @@ -212,6 +212,7 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { } lights.add(location); location.getWorld().setBlockData(location, light); + // DynamicLights.getInstance().getLogger().info("Added light at " + location); } public void removeLight(Location location) { @@ -223,7 +224,8 @@ public void removeLight(Location location) { b.setType(Material.AIR); } } - lights.remove(location); + // DynamicLights.getInstance().getLogger().info("Removed light at " + location); + // lights.remove(location); // it have been removed when planning the task. } // public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, From 10950dc7c261a165dd644d92126858ff05ade4c0 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sat, 14 Feb 2026 16:36:52 +0100 Subject: [PATCH 20/35] Folia fix: run find best block logic in region thread. --- CHANGELOG.md | 4 + build.gradle.kts | 3 +- .../dynamicLights/event/PlayerHandler.java | 2 +- .../dynamicLights/util/LightManager.java | 192 +++++++----------- 4 files changed, 75 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d9dab..02bea24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.4.3 + +Folia fix: run find best block logic in region thread. + # 1.4.2 Fix light location being compare between block & player eye wich was never the same making the light flicker. diff --git a/build.gradle.kts b/build.gradle.kts index 29b208f..3ab98f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.2" +version = "1.4.3" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" @@ -66,6 +66,7 @@ tasks { // Your plugin's jar (or shadowJar if present) will be used automatically. minecraftVersion(mainMinecraftVersion) } + runPaper.folia.registerTask() } val targetJavaVersion = 21 diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 38fd616..92405ed 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -36,5 +36,5 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { // } @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuitEvent(PlayerQuitEvent event) { DynamicLights.manager.removePlayer(event.getPlayer().getUniqueId()); } + public void onPlayerQuitEvent(PlayerQuitEvent event) { DynamicLights.manager.removeLight(event.getPlayer().getUniqueId()); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 07216cf..23fab87 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -1,14 +1,10 @@ package github.xCykrix.dynamicLights.util; import github.xCykrix.dynamicLights.DynamicLights; -import io.papermc.paper.threadedregions.scheduler.ScheduledTask; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -17,7 +13,6 @@ import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.type.Light; @@ -27,8 +22,7 @@ public class LightManager { private final LightSource source; - private final HashMap tasks = new HashMap<>(); - private final HashMap lastLightLocation = new HashMap<>(); + private final Map lastLightLocation = new ConcurrentHashMap<>(200); public final ConcurrentMap toggles; public final ConcurrentMap locks; @@ -37,8 +31,6 @@ public class LightManager { private JavaPlugin plugin; - private final List lights; - public LightManager(JavaPlugin plugin) { this.plugin = plugin; // YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); @@ -55,68 +47,65 @@ public LightManager(JavaPlugin plugin) { this.refresh = plugin.getConfig().getLong("update-rate"); this.toggle = plugin.getConfig().getBoolean("default-toggle-state"); - this.lights = Collections.synchronizedList(new LinkedList<>()); + // this.lights = Collections.synchronizedList(new LinkedList<>()); - // @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelay, long period, - // @NotNull TimeUnit unit); Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); } // @Override - public void shutdown() { clearLight(); } + public void shutdown() { clearAllLights(); } public void tick() { if (!plugin.isEnabled()) { - clearLight(); + clearAllLights(); return; } - // plugin.getLogger().info("tick with enabled plugin"); - - Set actualLocations = new HashSet<>(); - - // For each online player, check if we should add a light. - for (Player targetPlayer : Bukkit.getOnlinePlayers()) { - // plugin.getLogger().info("player " + targetPlayer.getName() + " is online"); - Optional opLocation = run(targetPlayer); - if (opLocation.isPresent()) { - actualLocations.add(opLocation.get()); - } + // For each online player, check if we should add a light or move the existing one + Collection players = Bukkit.getOnlinePlayers(); + for (Player targetPlayer : players) { + updatePlayerLight(targetPlayer); } - // Remove all old light except for the one in actualLocations - synchronized (this.lights) { - for (Location location : this.lights) { - if (!actualLocations.contains(location)) { - Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); - } - } - this.lights.clear(); - this.lights.addAll(actualLocations); + // // If there is some player UUID in the lastLightLocation that are not only anymore, remove the light & the player from the + // // lastLightLocation. + // for (Map.Entry entry : lastLightLocation.entrySet()) { + // if (!players.contains(Bukkit.getPlayer(entry.getKey()))) { + // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLight(entry.getKey())); + // } + // } + } + + public void clearAllLights() { + for (Map.Entry entry : lastLightLocation.entrySet()) { + Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLight(entry.getKey())); } } - public void clearLight() { - synchronized (this.lights) { - for (Location location : lights) { - Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(location)); - } - this.lights.clear(); + public void updatePlayerLight(Player player) { + Location playerLocation = player.getLocation(); + if (playerLocation != null) { // might be null. + Bukkit.getRegionScheduler().run(plugin, playerLocation, st -> updateLightToNewLocation(player)); } } - private Optional run(Player player) { + /** + * Find the best location to place a light and update the light if needed. + */ + private Optional updateLightToNewLocation(Player player) { if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { // plugin.getLogger().info("creative or spectator"); + this.removeLight(player.getUniqueId()); return Optional.empty(); } if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { - // plugin.getLogger().info("toggle false");plugin.getLogger().info + this.removeLight(player.getUniqueId()); return Optional.empty(); } + Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); @@ -127,22 +116,35 @@ private Optional run(Player player) { if (lightLevel > 0) { // plugin.getLogger().info("lightLevel > 0 " + lightLevel); Location eyeLocation = player.getEyeLocation(); - Bukkit.getRegionScheduler().run(plugin, eyeLocation, - st -> this.addLight(eyeLocation, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot))); - Location blockEyeLocation = getClosestAcceptableBlock(eyeLocation.getBlock()).getLocation(); - return Optional.of(blockEyeLocation); - } - // plugin.getLogger().info("lightLevel <= 0"); - return Optional.empty(); - } + Block bestBlock = getClosestAcceptableBlock(eyeLocation.getBlock()); + if (bestBlock == null) { + this.removeLight(player.getUniqueId()); + return Optional.empty(); + } + Location bestBlockLocation = bestBlock.getLocation(); + + // Update the light in Minecraft & int lastLightLocation + Location last = lastLightLocation.getOrDefault(player.getUniqueId(), null); + if (last != null) { + if (last.equals(bestBlockLocation) && bestBlock.getType() == Material.LIGHT + && bestBlock.getBlockData() instanceof Light existingLight && existingLight.getLevel() == lightLevel) { + // The light is already at the right location with the right level, no need to remove or to add + return Optional.empty(); + } + this.removeLight(player.getUniqueId()); + } - public void removePlayer(UUID uid) { - synchronized (this.tasks) { - if (this.tasks.containsKey(uid)) { - this.tasks.get(uid).cancel(); - this.tasks.remove(uid); + if (!player.isOnline()) { // player might have log off in the meantime + return Optional.empty(); } + lastLightLocation.put(player.getUniqueId(), bestBlockLocation); // replace ligh location + this.addLight(bestBlock, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot)); + + return Optional.of(bestBlockLocation); } + // plugin.getLogger().info("lightLevel <= 0"); + this.removeLight(player.getUniqueId()); + return Optional.empty(); } private boolean acceptableBlock(Block block) { @@ -163,36 +165,7 @@ private Block getClosestAcceptableBlock(Block block) { return null; } - public void addLight(Location location, int lightLevel, boolean isSubmersible) { - if (lightLevel == 0) { - return; - } - - - World world = location.getWorld(); - Block block = world.getBlockAt(location); - - - // Only AIR or LIGHT or WATER can be replaced. - block = getClosestAcceptableBlock(block); - - if (block == null) { - return; - } - - if (block.getType() == Material.LIGHT) { - // return if existing light level if the same - Light existingLight = (Light) block.getBlockData(); - if (existingLight.getLevel() == lightLevel) { - return; - - } - - } - - - location = block.getLocation(); - + public void addLight(Block block, int lightLevel, boolean isSubmersible) { Light light = (Light) Material.LIGHT.createBlockData(); switch (block.getType()) { case AIR, CAVE_AIR -> { @@ -208,14 +181,17 @@ public void addLight(Location location, int lightLevel, boolean isSubmersible) { } } default -> { + // do nothing } } - lights.add(location); - location.getWorld().setBlockData(location, light); - // DynamicLights.getInstance().getLogger().info("Added light at " + location); + + block.getWorld().setBlockData(block.getLocation(), light); + + // DynamicLights.getInstance().getLogger().info("Added light at " + block.getLocation()); } - public void removeLight(Location location) { + public void removeLight(UUID playerUuid) { + Location location = lastLightLocation.get(playerUuid); Block b = location.getWorld().getBlockAt(location); if (b.getBlockData() instanceof Light light) { if (light.isWaterlogged()) { @@ -224,42 +200,10 @@ public void removeLight(Location location) { b.setType(Material.AIR); } } + lastLightLocation.remove(playerUuid); // DynamicLights.getInstance().getLogger().info("Removed light at " + location); - // lights.remove(location); // it have been removed when planning the task. } - // public boolean valid(Player player, Material mainHand, Material offHand, Material helmet, Material chestplate, Material legging, - // Material boot) { - // boolean hasLightLevel = false; - // hasLightLevel = source.hasLightLevel(mainHand) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(offHand) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(helmet) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(chestplate) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(legging) ? true : hasLightLevel; - // hasLightLevel = source.hasLightLevel(boot) ? true : hasLightLevel; - - // if (!hasLightLevel) { - // return false; - // } - // Block currentLocation = player.getEyeLocation().getBlock(); - // if (currentLocation.getType() == Material.AIR || currentLocation.getType() == Material.CAVE_AIR) { - // return true; - // } - // if (currentLocation instanceof Waterlogged currentLocationWaterlogged && currentLocationWaterlogged.isWaterlogged()) { - // return false; - // } - // if (currentLocation.getType() == Material.WATER) { - // return source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot); - // } - // return false; - // } - - public Location getLastLocation(String uuid) { return lastLightLocation.getOrDefault(uuid, null); } - - public void setLastLocation(String uuid, Location location) { lastLightLocation.put(uuid, location); } - - public void removeLastLocation(String uuid) { lastLightLocation.remove(uuid); } - private Material getMaterialOrAir(ItemStack item) { if (item == null) return Material.AIR; From 5ec182c74d9e34537bf53e2935fc07bff084124a Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Sat, 14 Feb 2026 16:55:03 +0100 Subject: [PATCH 21/35] Fix player dying exception java.lang.NullPointerException: Cannot invoke "org.bukkit.Location.getWorld()" because "location" is null --- CHANGELOG.md | 4 ++ build.gradle.kts | 2 +- .../dynamicLights/event/PlayerHandler.java | 4 +- .../dynamicLights/util/LightManager.java | 48 +++++++++++++------ 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02bea24..078ea4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.4.4 + +Fix player dying exception + # 1.4.3 Folia fix: run find best block logic in region thread. diff --git a/build.gradle.kts b/build.gradle.kts index 3ab98f1..3076e85 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.3" +version = "1.4.4" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 92405ed..51b52d7 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -36,5 +36,7 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { // } @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuitEvent(PlayerQuitEvent event) { DynamicLights.manager.removeLight(event.getPlayer().getUniqueId()); } + public void onPlayerQuitEvent(PlayerQuitEvent event) { + DynamicLights.manager.removeLightFromLocationRegion(event.getPlayer().getUniqueId()); + } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 23fab87..88038be 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -13,6 +13,7 @@ import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.type.Light; @@ -69,18 +70,19 @@ public void tick() { updatePlayerLight(targetPlayer); } + // It's now done in player logout event. // // If there is some player UUID in the lastLightLocation that are not only anymore, remove the light & the player from the // // lastLightLocation. // for (Map.Entry entry : lastLightLocation.entrySet()) { // if (!players.contains(Bukkit.getPlayer(entry.getKey()))) { - // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLight(entry.getKey())); + // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); // } // } } public void clearAllLights() { for (Map.Entry entry : lastLightLocation.entrySet()) { - Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLight(entry.getKey())); + Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); } } @@ -97,11 +99,11 @@ public void updatePlayerLight(Player player) { private Optional updateLightToNewLocation(Player player) { if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { // plugin.getLogger().info("creative or spectator"); - this.removeLight(player.getUniqueId()); + this.removeLightFromLocationRegion(player.getUniqueId()); return Optional.empty(); } if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { - this.removeLight(player.getUniqueId()); + this.removeLightFromLocationRegion(player.getUniqueId()); return Optional.empty(); } @@ -118,10 +120,14 @@ private Optional updateLightToNewLocation(Player player) { Location eyeLocation = player.getEyeLocation(); Block bestBlock = getClosestAcceptableBlock(eyeLocation.getBlock()); if (bestBlock == null) { - this.removeLight(player.getUniqueId()); + this.removeLightFromLocationRegion(player.getUniqueId()); return Optional.empty(); } Location bestBlockLocation = bestBlock.getLocation(); + if (bestBlockLocation == null) { + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } // Update the light in Minecraft & int lastLightLocation Location last = lastLightLocation.getOrDefault(player.getUniqueId(), null); @@ -131,7 +137,7 @@ private Optional updateLightToNewLocation(Player player) { // The light is already at the right location with the right level, no need to remove or to add return Optional.empty(); } - this.removeLight(player.getUniqueId()); + this.removeLightFromLocationRegion(player.getUniqueId()); } if (!player.isOnline()) { // player might have log off in the meantime @@ -143,7 +149,7 @@ private Optional updateLightToNewLocation(Player player) { return Optional.of(bestBlockLocation); } // plugin.getLogger().info("lightLevel <= 0"); - this.removeLight(player.getUniqueId()); + this.removeLightFromLocationRegion(player.getUniqueId()); return Optional.empty(); } @@ -190,20 +196,34 @@ public void addLight(Block block, int lightLevel, boolean isSubmersible) { // DynamicLights.getInstance().getLogger().info("Added light at " + block.getLocation()); } - public void removeLight(UUID playerUuid) { + private void removeLight(UUID playerUuid) { Location location = lastLightLocation.get(playerUuid); - Block b = location.getWorld().getBlockAt(location); - if (b.getBlockData() instanceof Light light) { - if (light.isWaterlogged()) { - b.setType(Material.WATER); - } else { - b.setType(Material.AIR); + if (location != null) { + World world = location.getWorld(); + if (world != null) { + Block b = world.getBlockAt(location); + if (b.getBlockData() instanceof Light light) { + if (light.isWaterlogged()) { + b.setType(Material.WATER); + } else { + b.setType(Material.AIR); + } + } } } lastLightLocation.remove(playerUuid); // DynamicLights.getInstance().getLogger().info("Removed light at " + location); } + public void removeLightFromLocationRegion(UUID playerUuid) { + if (playerUuid == null || !lastLightLocation.containsKey(playerUuid)) { + return; + } + + Location location = lastLightLocation.get(playerUuid); + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(playerUuid)); + } + private Material getMaterialOrAir(ItemStack item) { if (item == null) return Material.AIR; From be63026b675eaa332eebeae8822a5c58c8901907 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 01:44:38 +0100 Subject: [PATCH 22/35] COPPER_TORCH --- src/main/resources/lights.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/lights.yml b/src/main/resources/lights.yml index 9a7ae7c..511ee9d 100644 --- a/src/main/resources/lights.yml +++ b/src/main/resources/lights.yml @@ -16,6 +16,7 @@ levels: PEARLESCENT_FROGLIGHT: 15 VERDANT_FROGLIGHT: 15 JACK_O_LANTERN: 15 + COPPER_TORCH: 9 # String List of Submersible Materials submersibles: From 012c38f2638bf6f1d1d44a4d51775cb56f2b4f20 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 01:47:24 +0100 Subject: [PATCH 23/35] More lockables --- src/main/resources/lights.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/lights.yml b/src/main/resources/lights.yml index 511ee9d..667d84f 100644 --- a/src/main/resources/lights.yml +++ b/src/main/resources/lights.yml @@ -36,6 +36,8 @@ lockables: - "SOUL_TORCH" - "LANTERN" - "SOUL_LANTERN" + - REDSTONE_TORCH + - COPPER_TORCH # DO NOT EDIT THE VERSION. Manual edits may corrupt or reset configuration files. # From ab3134e2c2f6b6450142b7f78f7385ae63317175 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 02:03:31 +0100 Subject: [PATCH 24/35] Fix light being removed from the new location instead of the old location. This was because of the asynch task to remove to old location. We now get the old location before running asych, then use that old location --- CHANGELOG.md | 7 +++---- build.gradle.kts | 2 +- .../github/xCykrix/dynamicLights/util/LightManager.java | 7 ++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 078ea4b..45c6e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,16 @@ -# 1.4.4 +# 1.4.5 +Fix light being removed from the new location instead of the old location. +# 1.4.4 Fix player dying exception # 1.4.3 - Folia fix: run find best block logic in region thread. # 1.4.2 - Fix light location being compare between block & player eye wich was never the same making the light flicker. # 1.3.4 - Update to 1.21.11 by using co.aikar:acf-paper instead of the custom NMS commands code from github.xCykrix:spigotdevkit. Reenable light toggle. Remove light-culling-distance config option as we now use real light that can be seen from any distance. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 3076e85..736c79b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.4" +version = "1.4.5" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 88038be..aceb1db 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -196,8 +196,8 @@ public void addLight(Block block, int lightLevel, boolean isSubmersible) { // DynamicLights.getInstance().getLogger().info("Added light at " + block.getLocation()); } - private void removeLight(UUID playerUuid) { - Location location = lastLightLocation.get(playerUuid); + private void removeLight(UUID playerUuid, Location location) { + // Location location = lastLightLocation.get(playerUuid); if (location != null) { World world = location.getWorld(); if (world != null) { @@ -221,7 +221,8 @@ public void removeLightFromLocationRegion(UUID playerUuid) { } Location location = lastLightLocation.get(playerUuid); - Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(playerUuid)); + // Since it's planned for later, we need to send the current last location before it will be override when placing the new light. + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(playerUuid, location)); } private Material getMaterialOrAir(ItemStack item) { From 1744d038ad7436fe997ed3da3dca391b00a5ab98 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 02:17:28 +0100 Subject: [PATCH 25/35] Remove unneeded getRegionScheduler().run inside a getRegionScheduler().run. --- .../dynamicLights/util/LightManager.java | 468 +++++++++--------- 1 file changed, 234 insertions(+), 234 deletions(-) diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index aceb1db..db94fb5 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -1,234 +1,234 @@ -package github.xCykrix.dynamicLights.util; - -import github.xCykrix.dynamicLights.DynamicLights; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.type.Light; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.java.JavaPlugin; - -public class LightManager { - private final LightSource source; - private final Map lastLightLocation = new ConcurrentHashMap<>(200); - - public final ConcurrentMap toggles; - public final ConcurrentMap locks; - private final long refresh; - public final boolean toggle; - - private JavaPlugin plugin; - - public LightManager(JavaPlugin plugin) { - this.plugin = plugin; - // YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); - // if (config == null) { - // throw new RuntimeException("config.yml is corrupted or contains invalid formatting. Failed to load plugin."); - // } - - this.source = DynamicLights.source; - // TODO reenable file storing of toggles & locks - // this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); - // this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); - this.toggles = new ConcurrentHashMap<>(); - this.locks = new ConcurrentHashMap<>(); - this.refresh = plugin.getConfig().getLong("update-rate"); - this.toggle = plugin.getConfig().getBoolean("default-toggle-state"); - - // this.lights = Collections.synchronizedList(new LinkedList<>()); - - Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); - } - - - // @Override - public void shutdown() { clearAllLights(); } - - - public void tick() { - if (!plugin.isEnabled()) { - clearAllLights(); - return; - } - - // For each online player, check if we should add a light or move the existing one - Collection players = Bukkit.getOnlinePlayers(); - for (Player targetPlayer : players) { - updatePlayerLight(targetPlayer); - } - - // It's now done in player logout event. - // // If there is some player UUID in the lastLightLocation that are not only anymore, remove the light & the player from the - // // lastLightLocation. - // for (Map.Entry entry : lastLightLocation.entrySet()) { - // if (!players.contains(Bukkit.getPlayer(entry.getKey()))) { - // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); - // } - // } - } - - public void clearAllLights() { - for (Map.Entry entry : lastLightLocation.entrySet()) { - Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); - } - } - - public void updatePlayerLight(Player player) { - Location playerLocation = player.getLocation(); - if (playerLocation != null) { // might be null. - Bukkit.getRegionScheduler().run(plugin, playerLocation, st -> updateLightToNewLocation(player)); - } - } - - /** - * Find the best location to place a light and update the light if needed. - */ - private Optional updateLightToNewLocation(Player player) { - if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { - // plugin.getLogger().info("creative or spectator"); - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - - - Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); - Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); - Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); - Material chestplate = getMaterialOrAir(player.getInventory().getChestplate()); - Material legging = getMaterialOrAir(player.getInventory().getLeggings()); - Material boot = getMaterialOrAir(player.getInventory().getBoots()); - int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); - if (lightLevel > 0) { - // plugin.getLogger().info("lightLevel > 0 " + lightLevel); - Location eyeLocation = player.getEyeLocation(); - Block bestBlock = getClosestAcceptableBlock(eyeLocation.getBlock()); - if (bestBlock == null) { - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - Location bestBlockLocation = bestBlock.getLocation(); - if (bestBlockLocation == null) { - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - - // Update the light in Minecraft & int lastLightLocation - Location last = lastLightLocation.getOrDefault(player.getUniqueId(), null); - if (last != null) { - if (last.equals(bestBlockLocation) && bestBlock.getType() == Material.LIGHT - && bestBlock.getBlockData() instanceof Light existingLight && existingLight.getLevel() == lightLevel) { - // The light is already at the right location with the right level, no need to remove or to add - return Optional.empty(); - } - this.removeLightFromLocationRegion(player.getUniqueId()); - } - - if (!player.isOnline()) { // player might have log off in the meantime - return Optional.empty(); - } - lastLightLocation.put(player.getUniqueId(), bestBlockLocation); // replace ligh location - this.addLight(bestBlock, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot)); - - return Optional.of(bestBlockLocation); - } - // plugin.getLogger().info("lightLevel <= 0"); - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - - private boolean acceptableBlock(Block block) { - Material type = block.getType(); - return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; - } - - private Block getClosestAcceptableBlock(Block block) { - List possibleLocation = List.of(block, block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), - block.getRelative(BlockFace.SOUTH), block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), - block.getRelative(BlockFace.DOWN)); - - for (Block relativeBlock : possibleLocation) { - if (acceptableBlock(relativeBlock)) { - return relativeBlock; - } - } - return null; - } - - public void addLight(Block block, int lightLevel, boolean isSubmersible) { - Light light = (Light) Material.LIGHT.createBlockData(); - switch (block.getType()) { - case AIR, CAVE_AIR -> { - light.setWaterlogged(false); - light.setLevel(lightLevel); - } - case WATER -> { - if (isSubmersible) { - light.setWaterlogged(true); - light.setLevel(lightLevel); - } else { - return; - } - } - default -> { - // do nothing - } - } - - block.getWorld().setBlockData(block.getLocation(), light); - - // DynamicLights.getInstance().getLogger().info("Added light at " + block.getLocation()); - } - - private void removeLight(UUID playerUuid, Location location) { - // Location location = lastLightLocation.get(playerUuid); - if (location != null) { - World world = location.getWorld(); - if (world != null) { - Block b = world.getBlockAt(location); - if (b.getBlockData() instanceof Light light) { - if (light.isWaterlogged()) { - b.setType(Material.WATER); - } else { - b.setType(Material.AIR); - } - } - } - } - lastLightLocation.remove(playerUuid); - // DynamicLights.getInstance().getLogger().info("Removed light at " + location); - } - - public void removeLightFromLocationRegion(UUID playerUuid) { - if (playerUuid == null || !lastLightLocation.containsKey(playerUuid)) { - return; - } - - Location location = lastLightLocation.get(playerUuid); - // Since it's planned for later, we need to send the current last location before it will be override when placing the new light. - Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(playerUuid, location)); - } - - private Material getMaterialOrAir(ItemStack item) { - if (item == null) - return Material.AIR; - else - return item.getType(); - } -} +package github.xCykrix.dynamicLights.util; + +import github.xCykrix.dynamicLights.DynamicLights; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.type.Light; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; + +public class LightManager { + private final LightSource source; + private final Map lastLightLocation = new ConcurrentHashMap<>(200); + + public final ConcurrentMap toggles; + public final ConcurrentMap locks; + private final long refresh; + public final boolean toggle; + + private JavaPlugin plugin; + + public LightManager(JavaPlugin plugin) { + this.plugin = plugin; + // YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); + // if (config == null) { + // throw new RuntimeException("config.yml is corrupted or contains invalid formatting. Failed to load plugin."); + // } + + this.source = DynamicLights.source; + // TODO reenable file storing of toggles & locks + // this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); + // this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); + this.toggles = new ConcurrentHashMap<>(); + this.locks = new ConcurrentHashMap<>(); + this.refresh = plugin.getConfig().getLong("update-rate"); + this.toggle = plugin.getConfig().getBoolean("default-toggle-state"); + + // this.lights = Collections.synchronizedList(new LinkedList<>()); + + Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); + } + + + // @Override + public void shutdown() { clearAllLights(); } + + + public void tick() { + if (!plugin.isEnabled()) { + clearAllLights(); + return; + } + + // For each online player, check if we should add a light or move the existing one + Collection players = Bukkit.getOnlinePlayers(); + for (Player targetPlayer : players) { + updatePlayerLight(targetPlayer); + } + + // It's now done in player logout event. + // // If there is some player UUID in the lastLightLocation that are not only anymore, remove the light & the player from the + // // lastLightLocation. + // for (Map.Entry entry : lastLightLocation.entrySet()) { + // if (!players.contains(Bukkit.getPlayer(entry.getKey()))) { + // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); + // } + // } + } + + public void clearAllLights() { + for (Map.Entry entry : lastLightLocation.entrySet()) { + this.removeLightFromLocationRegion(entry.getKey()); + } + } + + public void updatePlayerLight(Player player) { + Location playerLocation = player.getLocation(); + if (playerLocation != null) { // might be null. + Bukkit.getRegionScheduler().run(plugin, playerLocation, st -> updateLightToNewLocation(player)); + } + } + + /** + * Find the best location to place a light and update the light if needed. + */ + private Optional updateLightToNewLocation(Player player) { + if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { + // plugin.getLogger().info("creative or spectator"); + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } + if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } + + + Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); + Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); + Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); + Material chestplate = getMaterialOrAir(player.getInventory().getChestplate()); + Material legging = getMaterialOrAir(player.getInventory().getLeggings()); + Material boot = getMaterialOrAir(player.getInventory().getBoots()); + int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); + if (lightLevel > 0) { + // plugin.getLogger().info("lightLevel > 0 " + lightLevel); + Location eyeLocation = player.getEyeLocation(); + Block bestBlock = getClosestAcceptableBlock(eyeLocation.getBlock()); + if (bestBlock == null) { + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } + Location bestBlockLocation = bestBlock.getLocation(); + if (bestBlockLocation == null) { + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } + + // Update the light in Minecraft & int lastLightLocation + Location last = lastLightLocation.getOrDefault(player.getUniqueId(), null); + if (last != null) { + if (last.equals(bestBlockLocation) && bestBlock.getType() == Material.LIGHT + && bestBlock.getBlockData() instanceof Light existingLight && existingLight.getLevel() == lightLevel) { + // The light is already at the right location with the right level, no need to remove or to add + return Optional.empty(); + } + this.removeLightFromLocationRegion(player.getUniqueId()); + } + + if (!player.isOnline()) { // player might have log off in the meantime + return Optional.empty(); + } + lastLightLocation.put(player.getUniqueId(), bestBlockLocation); // replace ligh location + this.addLight(bestBlock, lightLevel, source.isSubmersible(mainHand, offHand, helmet, chestplate, legging, boot)); + + return Optional.of(bestBlockLocation); + } + // plugin.getLogger().info("lightLevel <= 0"); + this.removeLightFromLocationRegion(player.getUniqueId()); + return Optional.empty(); + } + + private boolean acceptableBlock(Block block) { + Material type = block.getType(); + return type == Material.AIR || type == Material.CAVE_AIR || type == Material.LIGHT || type == Material.WATER; + } + + private Block getClosestAcceptableBlock(Block block) { + List possibleLocation = List.of(block, block.getRelative(BlockFace.NORTH), block.getRelative(BlockFace.EAST), + block.getRelative(BlockFace.SOUTH), block.getRelative(BlockFace.WEST), block.getRelative(BlockFace.UP), + block.getRelative(BlockFace.DOWN)); + + for (Block relativeBlock : possibleLocation) { + if (acceptableBlock(relativeBlock)) { + return relativeBlock; + } + } + return null; + } + + public void addLight(Block block, int lightLevel, boolean isSubmersible) { + Light light = (Light) Material.LIGHT.createBlockData(); + switch (block.getType()) { + case AIR, CAVE_AIR -> { + light.setWaterlogged(false); + light.setLevel(lightLevel); + } + case WATER -> { + if (isSubmersible) { + light.setWaterlogged(true); + light.setLevel(lightLevel); + } else { + return; + } + } + default -> { + // do nothing + } + } + + block.getWorld().setBlockData(block.getLocation(), light); + + // DynamicLights.getInstance().getLogger().info("Added light at " + block.getLocation()); + } + + private void removeLight(UUID playerUuid, Location location) { + // Location location = lastLightLocation.get(playerUuid); + if (location != null) { + World world = location.getWorld(); + if (world != null) { + Block b = world.getBlockAt(location); + if (b.getBlockData() instanceof Light light) { + if (light.isWaterlogged()) { + b.setType(Material.WATER); + } else { + b.setType(Material.AIR); + } + } + } + } + lastLightLocation.remove(playerUuid); + // DynamicLights.getInstance().getLogger().info("Removed light at " + location); + } + + public void removeLightFromLocationRegion(UUID playerUuid) { + if (playerUuid == null || !lastLightLocation.containsKey(playerUuid)) { + return; + } + + Location location = lastLightLocation.get(playerUuid); + // Since it's planned for later, we need to send the current last location before it will be override when placing the new light. + Bukkit.getRegionScheduler().run(plugin, location, st -> this.removeLight(playerUuid, location)); + } + + private Material getMaterialOrAir(ItemStack item) { + if (item == null) + return Material.AIR; + else + return item.getType(); + } +} From 77df0ebc608a188d35545eff574ccb4910d6fb3b Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 02:45:03 +0100 Subject: [PATCH 26/35] Bigger number for update-rate sicne it's ms and not tick now. --- src/main/resources/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 006130f..36c9bd5 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ -# Update Rate: Controls how often the DynamicLights will tick. Defaults to 5 (4 times per second). Player heavy servers may require less often. -update-rate: 5 +# Update Rate: Controls how often the DynamicLights will tick. Defaults to 50ms. Player heavy servers may require less often. +update-rate: 50 # Default Toggle State: Controls if Dynamic Lighting is enabled by default. default-toggle-state: true From 4e12cdf96e7cfd678511d421edae34ddcb72511f Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Wed, 18 Feb 2026 02:46:31 +0100 Subject: [PATCH 27/35] Fix light removing --- CHANGELOG.md | 3 +++ build.gradle.kts | 2 +- .../java/github/xCykrix/dynamicLights/util/LightManager.java | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c6e43..1937680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.4.6 +Fix light removing + # 1.4.5 Fix light being removed from the new location instead of the old location. diff --git a/build.gradle.kts b/build.gradle.kts index 736c79b..53e5c6a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.5" +version = "1.4.6" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index db94fb5..122f6f1 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -211,7 +211,8 @@ private void removeLight(UUID playerUuid, Location location) { } } } - lastLightLocation.remove(playerUuid); + // Remove only if its the current last location, else we want to keep that value to remove it later when we will have to remove the light. + lastLightLocation.remove(playerUuid, location); // DynamicLights.getInstance().getLogger().info("Removed light at " + location); } From 4b94fd4346bbc673d53e26c197a3bc06e85c388d Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Thu, 19 Feb 2026 15:57:24 +0100 Subject: [PATCH 28/35] Fix not using the same location for region scheduler & inside the region scheduler as player eye location. --- CHANGELOG.md | 3 +++ build.gradle.kts | 2 +- .../dynamicLights/util/LightManager.java | 18 ++++-------------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1937680..38ad0e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.4.7 +Fix not using the same location for region scheduler & inside the region scheduler as player eye location. + # 1.4.6 Fix light removing diff --git a/build.gradle.kts b/build.gradle.kts index 53e5c6a..1aff7bf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.6" +version = "1.4.7" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 122f6f1..8fa301a 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -70,14 +70,7 @@ public void tick() { updatePlayerLight(targetPlayer); } - // It's now done in player logout event. - // // If there is some player UUID in the lastLightLocation that are not only anymore, remove the light & the player from the - // // lastLightLocation. - // for (Map.Entry entry : lastLightLocation.entrySet()) { - // if (!players.contains(Bukkit.getPlayer(entry.getKey()))) { - // Bukkit.getRegionScheduler().run(plugin, entry.getValue(), st -> this.removeLightFromLocationRegion(entry.getKey())); - // } - // } + // Removing player light when the player quit is now done in player quit event. } public void clearAllLights() { @@ -87,16 +80,14 @@ public void clearAllLights() { } public void updatePlayerLight(Player player) { - Location playerLocation = player.getLocation(); - if (playerLocation != null) { // might be null. - Bukkit.getRegionScheduler().run(plugin, playerLocation, st -> updateLightToNewLocation(player)); - } + Location playerLocation = player.getEyeLocation(); + Bukkit.getRegionScheduler().run(plugin, playerLocation, st -> updateLightToNewLocation(player, playerLocation)); } /** * Find the best location to place a light and update the light if needed. */ - private Optional updateLightToNewLocation(Player player) { + private Optional updateLightToNewLocation(Player player, Location eyeLocation) { if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { // plugin.getLogger().info("creative or spectator"); this.removeLightFromLocationRegion(player.getUniqueId()); @@ -117,7 +108,6 @@ private Optional updateLightToNewLocation(Player player) { int lightLevel = source.getLightLevel(mainHand, offHand, helmet, chestplate, legging, boot); if (lightLevel > 0) { // plugin.getLogger().info("lightLevel > 0 " + lightLevel); - Location eyeLocation = player.getEyeLocation(); Block bestBlock = getClosestAcceptableBlock(eyeLocation.getBlock()); if (bestBlock == null) { this.removeLightFromLocationRegion(player.getUniqueId()); From 034b51b9d0c60436ee6724c01d16f88dd3850f03 Mon Sep 17 00:00:00 2001 From: ewof Date: Sun, 1 Mar 2026 23:39:25 -0500 Subject: [PATCH 29/35] translations --- .../xCykrix/dynamicLights/DynamicLights.java | 21 ++- .../xCykrix/dynamicLights/Translations.java | 131 ++++++++++++++++++ .../command/DynamicLightsCommand.java | 1 + src/main/resources/config.yml | 11 +- src/main/resources/language.yml | 29 ---- src/main/resources/lights.yml | 32 ++--- src/main/resources/locale/en.yml | 23 +++ 7 files changed, 192 insertions(+), 56 deletions(-) create mode 100644 src/main/java/github/xCykrix/dynamicLights/Translations.java delete mode 100644 src/main/resources/language.yml create mode 100644 src/main/resources/locale/en.yml diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index 1eafe9b..96fc1ac 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -5,6 +5,13 @@ import github.xCykrix.dynamicLights.event.PlayerHandler; import github.xCykrix.dynamicLights.util.LightManager; import github.xCykrix.dynamicLights.util.LightSource; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.minimessage.translation.Argument; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.md_5.bungee.chat.TextComponentSerializer; + import org.bukkit.plugin.java.JavaPlugin; public final class DynamicLights extends JavaPlugin { @@ -20,6 +27,7 @@ public final class DynamicLights extends JavaPlugin { // public static LanguageFile language; public static LightSource source; public static LightManager manager; + private Translations translations; // @Override protected void pre() { @@ -34,9 +42,11 @@ public void initialize() { // save default config files saveDefaultConfig(); - saveResource("language.yml", false); saveResource("lights.yml", false); + this.translations = new Translations(); + translations.reload(); + // Register Configurations // configuration.register(new Resource("config.yml", null, this.getResource("config.yml"))) @@ -66,10 +76,13 @@ public void shutdown() { source.shutdown(); } - public static String translate(String key) { - // TODO fix language file access & color interpretation - return key; + public static Component translate(String key) { + return Component.translatable(key, Argument.string("prefix", PlainTextComponentSerializer.plainText().serialize(Component.translatable("prefix")))); } public static DynamicLights getInstance() { return DynamicLights.getPlugin(DynamicLights.class); } + + public Translations getTranslations() { + return translations; + } } diff --git a/src/main/java/github/xCykrix/dynamicLights/Translations.java b/src/main/java/github/xCykrix/dynamicLights/Translations.java new file mode 100644 index 0000000..62c399b --- /dev/null +++ b/src/main/java/github/xCykrix/dynamicLights/Translations.java @@ -0,0 +1,131 @@ +package github.xCykrix.dynamicLights; + +import com.google.common.collect.Maps; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore; +import net.kyori.adventure.translation.GlobalTranslator; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.codehaus.plexus.util.FileUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class Translations { + private final Path localeFolder = DynamicLights.getInstance().getDataPath().resolve("locale"); + private final Key name = Key.key("minigamelib", "locale"); + private MiniMessageTranslationStore storage = MiniMessageTranslationStore.create(name); + + public void reload() { + GlobalTranslator.translator().removeSource(storage); + storage = MiniMessageTranslationStore.create(name); + + if (!Files.isDirectory(localeFolder)) { + try { + Files.createDirectories(localeFolder); + } catch (Exception e) { + e.printStackTrace(); + return; + } + } + + // create default en.yml if not exists + Path defaultLocaleFile = localeFolder.resolve("en.yml"); + if (!Files.exists(defaultLocaleFile)) { + DynamicLights.getInstance().saveResource("locale/en.yml", false); + } + + List registered = new ArrayList<>(1); + registered.add(Locale.ENGLISH); + + try (var str = Files.list(localeFolder)) { + str.forEach(path -> { + String name = path.getFileName().toString(); + if (!name.endsWith(".yml")) { + return; + } + + String localeName = FileUtils.basename(name, ".yml"); + + Locale locale = Locale.forLanguageTag(localeName); + if (locale != null) load(locale); + registered.add(locale); + }); + } catch (Exception e) { + e.printStackTrace(); + } + + Map english = getEnglishKeys(); + + Locale.availableLocales().forEach(locale -> { + if (locale.getLanguage().isEmpty()) { + return; + } + + if (registered.contains(locale)) { + return; + } + + storage.registerAll(locale, english); + }); + + GlobalTranslator.translator().addSource(storage); + } + + public Component english(TranslatableComponent component) { + Component c = storage.translate(component, Locale.ENGLISH); + return c == null ? component : c; + } + + public Component translate(TranslatableComponent component, Locale locale) { + Component c = storage.translate(component, locale); + return c == null ? storage.translate(component, Locale.ENGLISH) : c; + } + + private void load(Locale locale) { + Path localeFile = localeFolder.resolve(locale.getLanguage() + ".yml"); + if (!Files.exists(localeFile)) { + try { + Files.createFile(localeFile); + } catch (Exception e) { + e.printStackTrace(); + return; + } + } + + Map map = Maps.newHashMap(); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(localeFile.toFile()); + + configuration.getValues(true).forEach((k, o) -> { + if (o instanceof MemorySection) { + return; + } + + map.put(k, (String) o); + }); + + storage.registerAll(locale, map); + } + + private Map getEnglishKeys() { + Map map = Maps.newHashMap(); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(localeFolder.resolve("en.yml").toFile()); + + configuration.getValues(true).forEach((k, o) -> { + if (o instanceof MemorySection) { + return; + } + + map.put(k, (String) o); + }); + + return map; + } +} diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java index 776a4f9..c4102b6 100644 --- a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -28,6 +28,7 @@ public static void onReload(CommandSender commandSender) { // Objects.requireNonNull(DynamicLights.configuration.getYAMLFile("lights.yml")).reload(); YamlConfiguration lights = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "lights.yml")); DynamicLights.source.initialize(); + DynamicLights.getInstance().getTranslations().reload(); commandSender.sendMessage(DynamicLights.translate("reload")); } catch (NullPointerException ex) { commandSender.sendMessage(DynamicLights.translate("reload-error")); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 36c9bd5..e8d9e37 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,12 +1,15 @@ -# Update Rate: Controls how often the DynamicLights will tick. Defaults to 50ms. Player heavy servers may require less often. -update-rate: 50 +# Update Rate: Controls how often the DynamicLights will tick. Defaults to 5 (4 times per second). Player heavy servers may require less often. +update-rate: 5 + +# Light Culling Distance: Controls how far away you should see another players DynamicLight. +light-culling-distance: 64 # Default Toggle State: Controls if Dynamic Lighting is enabled by default. default-toggle-state: true # Default Locking State: Controls if player's offhand are automatically locked by default. -default-lock-state: true +default-lock-state: false # DO NOT EDIT THE VERSION. Manual edits may corrupt or reset configuration files. # -version: 3 \ No newline at end of file +version: 3 diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml deleted file mode 100644 index 07b35b4..0000000 --- a/src/main/resources/language.yml +++ /dev/null @@ -1,29 +0,0 @@ -### -# Language File - Adventure API MiniMessage -# -# I recommend backing up this file in the event an update causes translations to be reset. -# -# General: https://docs.advntr.dev/minimessage/index.html -# Formatting: https://docs.advntr.dev/minimessage/format.html#minimessage-format -# -# Realtime Editor: https://webui.advntr.dev/ -### - -### -# Used as the Plugin's Chat and Console Prefix. Available in all "language." components as "". -### -chat-prefix: "[DynamicLights]" - -# Language Index -language: - prevent-block-place: " Light locking mode is currently enabled. You must sneak to place light sources from your Off Hand. This can be toggled with \"/dl lock\"." - enable-lock: " Enabled light lock mode." - disable-lock: " Disabled light lock mode." - toggle-on: " Light rendering enabled for client." - toggle-off: " Light rendering disabled for client." - reload: " Reloaded lights.yml configuration." - reload-error: " Failed to reload lights.yml configuration. Check Console.." - -# DO NOT EDIT THE VERSION. Manual edits may corrupt or reset configuration files. -# -version: 2 \ No newline at end of file diff --git a/src/main/resources/lights.yml b/src/main/resources/lights.yml index 667d84f..b0018dd 100644 --- a/src/main/resources/lights.yml +++ b/src/main/resources/lights.yml @@ -6,7 +6,7 @@ levels: LANTERN: 13 SOUL_LANTERN: 13 - TORCH: 11 + TORCH: 15 SOUL_TORCH: 11 REDSTONE_TORCH: 7 GLOWSTONE: 15 @@ -15,30 +15,24 @@ levels: OCHRE_FROGLIGHT: 15 PEARLESCENT_FROGLIGHT: 15 VERDANT_FROGLIGHT: 15 - JACK_O_LANTERN: 15 - COPPER_TORCH: 9 + COPPER_TORCH: 13 # String List of Submersible Materials submersibles: - - "GLOWSTONE" - - "SHROOMLIGHT" - - "SEA_LANTERN" - - "OCHRE_FROGLIGHT" - - "PEARLESCENT_FROGLIGHT" - - "VERDANT_FROGLIGHT" - - "JACK_O_LANTERN" - - +- GLOWSTONE +- SHROOMLIGHT +- SEA_LANTERN +- OCHRE_FROGLIGHT +- PEARLESCENT_FROGLIGHT +- VERDANT_FROGLIGHT # String List of Lockable Materials lockables: - - "TORCH" - - "SOUL_TORCH" - - "LANTERN" - - "SOUL_LANTERN" - - REDSTONE_TORCH - - COPPER_TORCH +- TORCH +- SOUL_TORCH +- LANTERN +- SOUL_LANTERN # DO NOT EDIT THE VERSION. Manual edits may corrupt or reset configuration files. # -version: 1 \ No newline at end of file +version: 1 diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml new file mode 100644 index 0000000..8180e4d --- /dev/null +++ b/src/main/resources/locale/en.yml @@ -0,0 +1,23 @@ +### +# Language File - Adventure API MiniMessage +# +# I recommend backing up this file in the event an update causes translations to be reset. +# +# General: https://docs.advntr.dev/minimessage/index.html +# Formatting: https://docs.advntr.dev/minimessage/format.html#minimessage-format +# +# Realtime Editor: https://webui.advntr.dev/ +### + +### +# Used as the Plugin's Chat and Console Prefix. Available in all "language." components as "". +### +prefix: "[DynamicLights]" + +prevent-block-place: " Light locking mode is currently enabled. You must sneak to place light sources from your Off Hand. This can be toggled with \"/dl lock\"." +enable-lock: " Enabled light lock mode." +disable-lock: " Disabled light lock mode." +toggle-on: " Light rendering enabled for client." +toggle-off: " Light rendering disabled for client." +reload: " Reloaded lights.yml configuration." +reload-error: " Failed to reload lights.yml configuration. Check Console.." From 85deb17ec04fbb45229d6edd4004d566782445d7 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Mon, 2 Mar 2026 10:12:49 +0100 Subject: [PATCH 30/35] Remove light-culling-distance from config that is not being used anymore. --- src/main/resources/config.yml | 3 --- src/main/resources/lights.yml | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e8d9e37..5405e6d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,9 +1,6 @@ # Update Rate: Controls how often the DynamicLights will tick. Defaults to 5 (4 times per second). Player heavy servers may require less often. update-rate: 5 -# Light Culling Distance: Controls how far away you should see another players DynamicLight. -light-culling-distance: 64 - # Default Toggle State: Controls if Dynamic Lighting is enabled by default. default-toggle-state: true diff --git a/src/main/resources/lights.yml b/src/main/resources/lights.yml index b0018dd..f25c9b7 100644 --- a/src/main/resources/lights.yml +++ b/src/main/resources/lights.yml @@ -15,6 +15,7 @@ levels: OCHRE_FROGLIGHT: 15 PEARLESCENT_FROGLIGHT: 15 VERDANT_FROGLIGHT: 15 + JACK_O_LANTERN: 15 COPPER_TORCH: 13 # String List of Submersible Materials From 48035c5f6b35c9bdadcef36830baeb2f956a6661 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Mon, 2 Mar 2026 11:48:19 +0100 Subject: [PATCH 31/35] Update plugin, french translation, only save each language translation & dynamic reload for translation. --- CHANGELOG.md | 3 +++ build.gradle.kts | 2 +- .../xCykrix/dynamicLights/DynamicLights.java | 10 ++++---- .../xCykrix/dynamicLights/Translations.java | 21 +++++++++++++---- src/main/resources/locale/en.yml | 4 ++-- src/main/resources/locale/fr.yml | 23 +++++++++++++++++++ 6 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 src/main/resources/locale/fr.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ad0e6..33867fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.5.0 +Fix translation & add French translation. + # 1.4.7 Fix not using the same location for region scheduler & inside the region scheduler as player eye location. diff --git a/build.gradle.kts b/build.gradle.kts index 1aff7bf..4666fe6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.4.7" +version = "1.5.0" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java index 96fc1ac..42f27f8 100644 --- a/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java +++ b/src/main/java/github/xCykrix/dynamicLights/DynamicLights.java @@ -6,12 +6,8 @@ import github.xCykrix.dynamicLights.util.LightManager; import github.xCykrix.dynamicLights.util.LightSource; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.minimessage.translation.Argument; -import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import net.md_5.bungee.chat.TextComponentSerializer; - import org.bukkit.plugin.java.JavaPlugin; public final class DynamicLights extends JavaPlugin { @@ -70,6 +66,12 @@ public void initialize() { @Override public void onEnable() { initialize(); } + @Override + public void reloadConfig() { + super.reloadConfig(); + translations.reload(); + } + // @Override public void shutdown() { manager.shutdown(); diff --git a/src/main/java/github/xCykrix/dynamicLights/Translations.java b/src/main/java/github/xCykrix/dynamicLights/Translations.java index 62c399b..1f9df31 100644 --- a/src/main/java/github/xCykrix/dynamicLights/Translations.java +++ b/src/main/java/github/xCykrix/dynamicLights/Translations.java @@ -36,11 +36,15 @@ public void reload() { } } - // create default en.yml if not exists - Path defaultLocaleFile = localeFolder.resolve("en.yml"); - if (!Files.exists(defaultLocaleFile)) { - DynamicLights.getInstance().saveResource("locale/en.yml", false); - } + // create default translation if not exists + Locale.availableLocales().forEach(locale -> { + String languageKey = locale.toString(); + Path defaultLocaleFile = localeFolder.resolve(languageKey + ".yml"); + String outLocalePath = "locale/" + languageKey + ".yml"; + if (!Files.exists(defaultLocaleFile) && DynamicLights.getInstance().getResource(outLocalePath) != null) { + DynamicLights.getInstance().saveResource(outLocalePath, false); + } + }); List registered = new ArrayList<>(1); registered.add(Locale.ENGLISH); @@ -57,6 +61,7 @@ public void reload() { Locale locale = Locale.forLanguageTag(localeName); if (locale != null) load(locale); registered.add(locale); + DynamicLights.getInstance().getLogger().info("Loaded locale: " + localeName); }); } catch (Exception e) { e.printStackTrace(); @@ -73,6 +78,11 @@ public void reload() { return; } + // Do not save language for each country, just save the country less version. + if(!locale.getLanguage().equals(locale.toString())) { + return; + } + storage.registerAll(locale, english); }); @@ -86,6 +96,7 @@ public Component english(TranslatableComponent component) { public Component translate(TranslatableComponent component, Locale locale) { Component c = storage.translate(component, locale); + DynamicLights.getInstance().getLogger().info("Translated: " + component.toString() + " -> " + c.toString()); return c == null ? storage.translate(component, Locale.ENGLISH) : c; } diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml index 8180e4d..5309186 100644 --- a/src/main/resources/locale/en.yml +++ b/src/main/resources/locale/en.yml @@ -17,7 +17,7 @@ prefix: "[DynamicLights] Light locking mode is currently enabled. You must sneak to place light sources from your Off Hand. This can be toggled with \"/dl lock\"." enable-lock: " Enabled light lock mode." disable-lock: " Disabled light lock mode." -toggle-on: " Light rendering enabled for client." -toggle-off: " Light rendering disabled for client." +toggle-on: " Light rendering enabled." +toggle-off: " Light rendering disabled." reload: " Reloaded lights.yml configuration." reload-error: " Failed to reload lights.yml configuration. Check Console.." diff --git a/src/main/resources/locale/fr.yml b/src/main/resources/locale/fr.yml new file mode 100644 index 0000000..a1d6bf8 --- /dev/null +++ b/src/main/resources/locale/fr.yml @@ -0,0 +1,23 @@ +### +# Language File - Adventure API MiniMessage +# +# I recommend backing up this file in the event an update causes translations to be reset. +# +# General: https://docs.advntr.dev/minimessage/index.html +# Formatting: https://docs.advntr.dev/minimessage/format.html#minimessage-format +# +# Realtime Editor: https://webui.advntr.dev/ +### + +### +# Used as the Plugin's Chat and Console Prefix. Available in all "language." components as "". +### +prefix: "[DynamicLights]" + +prevent-block-place: " Le mode de verrouillage des sources lumineuses est actuellement activé. Vous devez vous accroupir pour placer des sources lumineuses depuis votre main secondaire. Ce mode peut être désactivé avec la commande « /dl lock »." +enable-lock: " Mode de verrouillage lumineux activé." +disable-lock: " Mode de verrouillage lumineux désactivé." +toggle-on: " Rendu de la lumière activé." +toggle-off: " Rendu de la lumière désactivé." +reload: " Configuration lights.yml rechargée." +reload-error: " Échec du rechargement de la configuration lights.yml. Consultez la console.." From d9de2a2794516b9e2b2d3a0dfabee4dec7681f5b Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Tue, 3 Mar 2026 10:01:22 +0100 Subject: [PATCH 32/35] dynamiclights.lock default: true --- build.gradle.kts | 2 +- src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4666fe6..6391c86 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.5.0" +version = "1.5.1" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6da9f99..6bd899f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -12,7 +12,7 @@ folia-supported: true permissions: dynamiclights.lock: description: "Toggle placing light sources from your off hand." - default: false + default: true dynamiclights.toggle: description: "Toggle rendering of light sources for your client." default: true From 0c7bf1ef56c267d6e36da912475ebf658da938b6 Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Tue, 3 Mar 2026 20:59:04 +0100 Subject: [PATCH 33/35] Improve perf & save toggle/lock on player PDC. Perf are improved by updating the light on a player list that contains player with the right gamemode & toggle true. Data are saved on the player persistant data container to avoid having to manipulate external data file. --- build.gradle.kts | 2 +- .../command/DynamicLightsCommand.java | 26 ++++--- .../dynamicLights/event/PlayerHandler.java | 24 ++++--- .../dynamicLights/util/LightManager.java | 70 +++++++++---------- .../dynamicLights/util/PlayerUtil.java | 27 +++++++ 5 files changed, 89 insertions(+), 60 deletions(-) create mode 100644 src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java diff --git a/build.gradle.kts b/build.gradle.kts index 6391c86..86a6685 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "github.xCykrix" -version = "1.5.1" +version = "1.6.0" description="Dynamic Lights for Minecraft Servers without requiring Modding." val mainMinecraftVersion = "1.21.11" val supportedMinecraftVersions = "1.21.11 - 1.21.11" diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java index c4102b6..79a106d 100644 --- a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -5,6 +5,7 @@ import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Subcommand; import github.xCykrix.dynamicLights.DynamicLights; +import github.xCykrix.dynamicLights.util.PlayerUtil; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; @@ -16,6 +17,7 @@ @CommandAlias("dynamiclights|dynamiclight|dl") public class DynamicLightsCommand extends co.aikar.commands.BaseCommand {// extends DevkitSimpleState { + private static JavaPlugin plugin; public DynamicLightsCommand(JavaPlugin plugin) { this.plugin = plugin; } @@ -52,15 +54,13 @@ public static String getStackTrace(Throwable throwable) { @CommandPermission("dynamiclights.toggle") public static void onToggle(CommandSender commandSender) { if (commandSender instanceof Player player) { - String uuid = player.getUniqueId().toString(); - boolean current = DynamicLights.manager.toggles.getOrDefault(uuid, DynamicLights.manager.toggle); - if (!current) { - player.sendMessage(DynamicLights.translate("toggle-on")); - DynamicLights.manager.toggles.put(uuid, true); - } else { + if (PlayerUtil.getToggleStatus(player)) { player.sendMessage(DynamicLights.translate("toggle-off")); - DynamicLights.manager.toggles.put(uuid, false); + } else { + player.sendMessage(DynamicLights.translate("toggle-on")); } + + PlayerUtil.switchToggleStatus(player); } } @@ -69,15 +69,13 @@ public static void onToggle(CommandSender commandSender) { @CommandPermission("dynamiclights.lock") public static void onLock(CommandSender commandSender) { if (commandSender instanceof Player player) { - String uuid = player.getUniqueId().toString(); - boolean current = DynamicLights.manager.locks.getOrDefault(uuid, true); - if (!current) { - player.sendMessage(DynamicLights.translate("enable-lock")); - DynamicLights.manager.locks.put(uuid, true); + if (PlayerUtil.getLockStatus(player)) { + player.sendMessage(DynamicLights.translate("lock-off")); } else { - player.sendMessage(DynamicLights.translate("disable-lock")); - DynamicLights.manager.locks.put(uuid, false); + player.sendMessage(DynamicLights.translate("lock-on")); } + + PlayerUtil.switchLockStatus(player); } } } diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 51b52d7..24b85c9 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -1,10 +1,13 @@ package github.xCykrix.dynamicLights.event; import github.xCykrix.dynamicLights.DynamicLights; +import github.xCykrix.dynamicLights.util.PlayerUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.plugin.java.JavaPlugin; @@ -22,21 +25,26 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { if (!DynamicLights.source.isProtectedLight(event.getItemInHand().getType())) { return; } - if (DynamicLights.manager.locks.getOrDefault(event.getPlayer().getUniqueId().toString(), DynamicLights.manager.toggle)) { + if (PlayerUtil.getLockStatus(event.getPlayer())) { event.getPlayer().sendMessage(DynamicLights.translate("prevent-block-place")); event.setCancelled(true); } } } - // No need we do it for each online player - // @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - // public void onPlayerJoinEvent(PlayerJoinEvent event) { - // DynamicLights.manager.addPlayer(event.getPlayer()); - // } - @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuitEvent(PlayerQuitEvent event) { - DynamicLights.manager.removeLightFromLocationRegion(event.getPlayer().getUniqueId()); + DynamicLights.manager.removePlayerLightEnabled(event.getPlayer().getUniqueId()); + // DynamicLights.manager.removeLightFromLocationRegion(event.getPlayer().getUniqueId()); + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerGameModeChangeEvent(PlayerGameModeChangeEvent event) { + DynamicLights.manager.updatePlayerState(event.getPlayer(), event.getNewGameMode()); + } + + @EventHandler + public void onPlayerJoinEvent(PlayerJoinEvent event) { + DynamicLights.manager.updatePlayerState(event.getPlayer(), event.getPlayer().getGameMode()); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java index 8fa301a..8e10897 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/LightManager.java @@ -1,13 +1,12 @@ package github.xCykrix.dynamicLights.util; import github.xCykrix.dynamicLights.DynamicLights; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -25,38 +24,38 @@ public class LightManager { private final LightSource source; private final Map lastLightLocation = new ConcurrentHashMap<>(200); - public final ConcurrentMap toggles; - public final ConcurrentMap locks; - private final long refresh; - public final boolean toggle; + private final Set playerLightEnabled; private JavaPlugin plugin; public LightManager(JavaPlugin plugin) { this.plugin = plugin; - // YamlDocument config = DynamicLights.configuration.getYAMLFile("config.yml"); - // if (config == null) { - // throw new RuntimeException("config.yml is corrupted or contains invalid formatting. Failed to load plugin."); - // } - this.source = DynamicLights.source; - // TODO reenable file storing of toggles & locks - // this.toggles = DynamicLights.h2.get().openMap("lightToggleStatus"); - // this.locks = DynamicLights.h2.get().openMap("lightLockStatus"); - this.toggles = new ConcurrentHashMap<>(); - this.locks = new ConcurrentHashMap<>(); - this.refresh = plugin.getConfig().getLong("update-rate"); - this.toggle = plugin.getConfig().getBoolean("default-toggle-state"); - // this.lights = Collections.synchronizedList(new LinkedList<>()); + playerLightEnabled = ConcurrentHashMap.newKeySet(); - Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, refresh, TimeUnit.MILLISECONDS); + Bukkit.getAsyncScheduler().runAtFixedRate(plugin, st -> tick(), 0L, plugin.getConfig().getLong("update-rate"), TimeUnit.MILLISECONDS); } // @Override public void shutdown() { clearAllLights(); } + public void addPlayerLightEnabled(UUID uuid) { playerLightEnabled.add(uuid); } + + public void removePlayerLightEnabled(UUID uuid) { playerLightEnabled.remove(uuid); } + + public boolean isPlayerLightEnabled(UUID uuid) { return playerLightEnabled.contains(uuid); } + + // Player emit light if (SURVIVAL or ADVENTURE) and they have it enabled. + public void updatePlayerState(Player player, GameMode mode) { + if ((mode == GameMode.SURVIVAL || mode == GameMode.ADVENTURE) && PlayerUtil.getToggleStatus(player)) { + addPlayerLightEnabled(player.getUniqueId()); + } else { + removePlayerLightEnabled(player.getUniqueId()); + // removeLightFromLocationRegion(player.getUniqueId()); + } + } public void tick() { if (!plugin.isEnabled()) { @@ -64,13 +63,20 @@ public void tick() { return; } - // For each online player, check if we should add a light or move the existing one - Collection players = Bukkit.getOnlinePlayers(); - for (Player targetPlayer : players) { - updatePlayerLight(targetPlayer); + // For each online player that have the right game mode, check if we should add a light or move the existing one + for (UUID targetPlayerUUID : playerLightEnabled) { + updatePlayerLight(Bukkit.getPlayer(targetPlayerUUID)); } - // Removing player light when the player quit is now done in player quit event. + // Remove each light that should not be there + // With a short delay between each tick(), the light is added back when removed from somewhere else, + // so we need to remove it after the main for loop. + // It might call to remove the light many time if the region scheduler is not fast enough, but it's fine I guess. + for (UUID targetPlayerUUID : lastLightLocation.keySet()) { + if (!playerLightEnabled.contains(targetPlayerUUID)) { + this.removeLightFromLocationRegion(targetPlayerUUID); + } + } } public void clearAllLights() { @@ -88,17 +94,6 @@ public void updatePlayerLight(Player player) { * Find the best location to place a light and update the light if needed. */ private Optional updateLightToNewLocation(Player player, Location eyeLocation) { - if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { - // plugin.getLogger().info("creative or spectator"); - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - if (!(this.toggles.getOrDefault(player.getUniqueId().toString(), this.toggle))) { - this.removeLightFromLocationRegion(player.getUniqueId()); - return Optional.empty(); - } - - Material mainHand = getMaterialOrAir(player.getInventory().getItemInMainHand()); Material offHand = getMaterialOrAir(player.getInventory().getItemInOffHand()); Material helmet = getMaterialOrAir(player.getInventory().getHelmet()); @@ -201,7 +196,8 @@ private void removeLight(UUID playerUuid, Location location) { } } } - // Remove only if its the current last location, else we want to keep that value to remove it later when we will have to remove the light. + // Remove only if its the current last location, else we want to keep that value to remove it later when we will have to remove the + // light. lastLightLocation.remove(playerUuid, location); // DynamicLights.getInstance().getLogger().info("Removed light at " + location); } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java b/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java new file mode 100644 index 0000000..61df9d7 --- /dev/null +++ b/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java @@ -0,0 +1,27 @@ +package github.xCykrix.dynamicLights.util; + +import github.xCykrix.dynamicLights.DynamicLights; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; + +public class PlayerUtil { + private static NamespacedKey lightToggleStatusKey = new NamespacedKey("dynamiclights", "toggle-state"); + private static NamespacedKey lightLockStatusKey = new NamespacedKey("dynamiclights", "lock-state"); + public static boolean getToggleStatus(Player player) { + return player.getPersistentDataContainer().getOrDefault(lightToggleStatusKey, PersistentDataType.BOOLEAN, + DynamicLights.getInstance().getConfig().getBoolean("default-toggle-state")); + } + public static void switchToggleStatus(Player player) { + player.getPersistentDataContainer().set(lightToggleStatusKey, PersistentDataType.BOOLEAN, !getToggleStatus(player)); + } + + public static boolean getLockStatus(Player player) { + return player.getPersistentDataContainer().getOrDefault(lightLockStatusKey, PersistentDataType.BOOLEAN, + DynamicLights.getInstance().getConfig().getBoolean("default-lock-state")); + } + + public static void switchLockStatus(Player player) { + player.getPersistentDataContainer().set(lightLockStatusKey, PersistentDataType.BOOLEAN, !getLockStatus(player)); + } +} From 128dce8525c6b34903570e4650d5672a6d5e3d3d Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Tue, 3 Mar 2026 21:19:21 +0100 Subject: [PATCH 34/35] Add an option to disable the light lock message. Done #3 --- CHANGELOG.md | 5 +++++ .../command/DynamicLightsCommand.java | 18 ++++++++++++++++-- .../dynamicLights/event/PlayerHandler.java | 6 +++++- .../xCykrix/dynamicLights/util/PlayerUtil.java | 14 ++++++++++++-- src/main/resources/config.yml | 3 +++ src/main/resources/locale/en.yml | 4 +++- src/main/resources/locale/fr.yml | 8 +++++--- 7 files changed, 49 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33867fb..a84363d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.6.0 +Improve performances. +Save user settings on the Minecraft world. +Add an option to disable the light lock message. + # 1.5.0 Fix translation & add French translation. diff --git a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java index 79a106d..32c001d 100644 --- a/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java +++ b/src/main/java/github/xCykrix/dynamicLights/command/DynamicLightsCommand.java @@ -70,12 +70,26 @@ public static void onToggle(CommandSender commandSender) { public static void onLock(CommandSender commandSender) { if (commandSender instanceof Player player) { if (PlayerUtil.getLockStatus(player)) { - player.sendMessage(DynamicLights.translate("lock-off")); + player.sendMessage(DynamicLights.translate("disable-lock")); } else { - player.sendMessage(DynamicLights.translate("lock-on")); + player.sendMessage(DynamicLights.translate("enable-lock")); } PlayerUtil.switchLockStatus(player); } } + @Subcommand("lockverbose") + @Description("Make the warning lock message go away") + @CommandPermission("dynamiclights.lock") + public static void onLockVerbose(CommandSender commandSender) { + if (commandSender instanceof Player player) { + if (PlayerUtil.getLockStatus(player)) { + player.sendMessage(DynamicLights.translate("disable-lock-verbose")); + } else { + player.sendMessage(DynamicLights.translate("enable-lock-verbose")); + } + + PlayerUtil.switchLockVerboseStatus(player); + } + } } diff --git a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java index 24b85c9..9a7000b 100644 --- a/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java +++ b/src/main/java/github/xCykrix/dynamicLights/event/PlayerHandler.java @@ -26,7 +26,11 @@ public void playerBlockPlaceEvent(BlockPlaceEvent event) { return; } if (PlayerUtil.getLockStatus(event.getPlayer())) { - event.getPlayer().sendMessage(DynamicLights.translate("prevent-block-place")); + // send a text to the player to explain why the block place event was cancelled + if (PlayerUtil.getLockVerboseStatus(event.getPlayer())) { + event.getPlayer().sendMessage(DynamicLights.translate("prevent-block-place")); + } + // cancel the event event.setCancelled(true); } } diff --git a/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java b/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java index 61df9d7..e6c756c 100644 --- a/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java +++ b/src/main/java/github/xCykrix/dynamicLights/util/PlayerUtil.java @@ -8,9 +8,10 @@ public class PlayerUtil { private static NamespacedKey lightToggleStatusKey = new NamespacedKey("dynamiclights", "toggle-state"); private static NamespacedKey lightLockStatusKey = new NamespacedKey("dynamiclights", "lock-state"); + private static NamespacedKey lightLockVerboseStatusKey = new NamespacedKey("dynamiclights", "lock-verbose-state"); public static boolean getToggleStatus(Player player) { return player.getPersistentDataContainer().getOrDefault(lightToggleStatusKey, PersistentDataType.BOOLEAN, - DynamicLights.getInstance().getConfig().getBoolean("default-toggle-state")); + DynamicLights.getInstance().getConfig().getBoolean("default-toggle-state", true)); } public static void switchToggleStatus(Player player) { player.getPersistentDataContainer().set(lightToggleStatusKey, PersistentDataType.BOOLEAN, !getToggleStatus(player)); @@ -18,10 +19,19 @@ public static void switchToggleStatus(Player player) { public static boolean getLockStatus(Player player) { return player.getPersistentDataContainer().getOrDefault(lightLockStatusKey, PersistentDataType.BOOLEAN, - DynamicLights.getInstance().getConfig().getBoolean("default-lock-state")); + DynamicLights.getInstance().getConfig().getBoolean("default-lock-state", false)); } public static void switchLockStatus(Player player) { player.getPersistentDataContainer().set(lightLockStatusKey, PersistentDataType.BOOLEAN, !getLockStatus(player)); } + + public static boolean getLockVerboseStatus(Player player) { + return player.getPersistentDataContainer().getOrDefault(lightLockVerboseStatusKey, PersistentDataType.BOOLEAN, + DynamicLights.getInstance().getConfig().getBoolean("default-lock-verbose-state", true)); + } + + public static void switchLockVerboseStatus(Player player) { + player.getPersistentDataContainer().set(lightLockVerboseStatusKey, PersistentDataType.BOOLEAN, !getLockVerboseStatus(player)); + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5405e6d..fe254c7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -7,6 +7,9 @@ default-toggle-state: true # Default Locking State: Controls if player's offhand are automatically locked by default. default-lock-state: false +# Default warn player when locked that they can unlock it with a command. +default-lock-verbose-state: true + # DO NOT EDIT THE VERSION. Manual edits may corrupt or reset configuration files. # version: 3 diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml index 5309186..954ecaa 100644 --- a/src/main/resources/locale/en.yml +++ b/src/main/resources/locale/en.yml @@ -14,9 +14,11 @@ ### prefix: "[DynamicLights]" -prevent-block-place: " Light locking mode is currently enabled. You must sneak to place light sources from your Off Hand. This can be toggled with \"/dl lock\"." +prevent-block-place: " Light locking mode is currently enabled. You must sneak to place light sources from your Off Hand. This can be toggled with \"/dl lock\". (Disable this message with \"/dl lockverbose\".)" enable-lock: " Enabled light lock mode." disable-lock: " Disabled light lock mode." +enable-lock-verbose: " Enabled light lock mode warning message." +disable-lock-verbose: " Disabled light lock mode warning message." toggle-on: " Light rendering enabled." toggle-off: " Light rendering disabled." reload: " Reloaded lights.yml configuration." diff --git a/src/main/resources/locale/fr.yml b/src/main/resources/locale/fr.yml index a1d6bf8..76d0482 100644 --- a/src/main/resources/locale/fr.yml +++ b/src/main/resources/locale/fr.yml @@ -14,9 +14,11 @@ ### prefix: "[DynamicLights]" -prevent-block-place: " Le mode de verrouillage des sources lumineuses est actuellement activé. Vous devez vous accroupir pour placer des sources lumineuses depuis votre main secondaire. Ce mode peut être désactivé avec la commande « /dl lock »." -enable-lock: " Mode de verrouillage lumineux activé." -disable-lock: " Mode de verrouillage lumineux désactivé." +prevent-block-place: " Le mode de verrouillage des sources lumineuses est actuellement activé. Vous devez vous accroupir pour placer des sources lumineuses depuis votre main secondaire. Ce mode peut être désactivé avec « /dl lock ». (Désactivez ce message avec « /dl lockverbose ».)" +enable-lock: " Mode de verrouillage de la lumière activé." +disable-lock: " Mode de verrouillage de la lumière désactivé." +enable-lock-verbose: " Message d'avertissement du mode de verrouillage de la lumière activé." +disable-lock-verbose: " Message d'avertissement du mode de verrouillage de la lumière désactivé." toggle-on: " Rendu de la lumière activé." toggle-off: " Rendu de la lumière désactivé." reload: " Configuration lights.yml rechargée." From f30876be2eda962bcf1ab8b439ac6e2e346b749d Mon Sep 17 00:00:00 2001 From: HydrolienF Date: Tue, 3 Mar 2026 21:25:26 +0100 Subject: [PATCH 35/35] Action to publish to github --- .github/workflows/release.yml | 82 +++++++++++++++++++++++++++++++++ build.gradle.kts | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6d847a5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,82 @@ +name: Build and Publish Release +on: + push: + tags: + - '*' +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: gradle + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + # build + - name: Build with Gradle Wrapper + run: ./gradlew assemble + env: + MVNDI_MVN_USER: ${{ secrets.MVNDI_MVN_USER }} + MVNDI_MVN_KEY: ${{ secrets.MVNDI_MVN_KEY }} + - name: Publish to Remote + run: ./gradlew publish + env: + MVNDI_MVN_USER: ${{ secrets.MVNDI_MVN_USER }} + MVNDI_MVN_KEY: ${{ secrets.MVNDI_MVN_KEY }} + + - name: Get version & release name + id: version + run: | + echo "version=$(./gradlew -q echoVersion)" >> $GITHUB_OUTPUT + echo "releaseName=$(./gradlew -q echoReleaseName)" >> $GITHUB_OUTPUT + + - name: Print version & release name + run: | + echo "Version: ${{ steps.version.outputs.version }}" + echo "Release Name: ${{ steps.version.outputs.releaseName }}" + + + - name: Create an empty CHANGELOG.md if missing + run: | + if [ ! -f CHANGELOG.md ]; then + touch CHANGELOG.md + fi + + - name: Read the first lines of CHANGELOG.md from gradle + id: changelog + run: | + { + echo "changelog<> $GITHUB_OUTPUT + + + - name: Create Release + id: createRelease + uses: ncipollo/release-action@v1.14.0 + with: + allowUpdates: true + updateOnlyUnreleased: true + artifacts: build/libs/${{ github.event.repository.name }}-${{ steps.version.outputs.version }}.jar + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ steps.version.outputs.version }} + name: ${{ steps.version.outputs.releaseName }} + body: ${{ steps.changelog.outputs.changelog }} + # prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }} #Always release for now + skipIfReleaseExists: true + + # - name: Publish to hangar & modrinth + # env: + # # Make sure you have added the repository secrets in the repository's settings + # HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }} + # MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} + # run: ./gradlew assemble publishPluginPublicationToHangar modrinth \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 86a6685..069df99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,12 @@ tasks.named("shadowJar") { archiveClassifier = null; } +publishing { + publications.create("maven") { + from(components["java"]) + } +} + tasks { assemble { dependsOn(shadowJar) @@ -78,3 +84,83 @@ java { toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) } } + +tasks.register("echoVersion") { + group = "documentation" + description = "Displays the version." + doLast { + println("${project.version}") + } +} + +tasks.register("echoReleaseName") { + group = "documentation" + description = "Displays the release name." + doLast { + println("${project.version} [${supportedMinecraftVersions}]") + } +} + +val extractChangelog = tasks.register("extractChangelog") { + group = "documentation" + description = "Extracts the changelog for the current project version from CHANGELOG.md, including the version header." + + val changelog: Property = project.objects.property(String::class) + outputs.upToDateWhen { false } + + doLast { + val version = project.version.toString() + val changelogFile = project.file("CHANGELOG.md") + + if (!changelogFile.exists()) { + println("CHANGELOG.md not found.") + changelog.set("No changelog found.") + return@doLast + } + + val lines = changelogFile.readLines() + val entries = mutableListOf() + var foundVersion = false + + for (line in lines) { + when { + // Include the version line itself + line.trim().equals("# $version", ignoreCase = true) -> { + foundVersion = true + entries.add(line) + } + // Stop collecting at the next version header + foundVersion && line.trim().startsWith("# ") -> break + // Collect lines after the version header + foundVersion -> entries.add(line) + } + } + + val result = if (entries.isEmpty()) { + "Update to $version." + } else { + entries.joinToString("\n").trim() + } + + // println("Changelog for version $version:\n$result") + changelog.set(result) + } + + // Make changelog accessible from other tasks + extensions.add(Property::class.java, "changelog", changelog) +} + +tasks.register("echoLatestVersionChangelog") { + group = "documentation" + description = "Displays the latest version change." + + dependsOn(tasks.named("extractChangelog")) + + doLast { + println((extractChangelog.get().extensions.findByType(Property::class.java) as Property).get()) + } +} + + +val versionString: String = version as String +val isRelease: Boolean = !versionString.contains("SNAPSHOT") \ No newline at end of file