first commit

This commit is contained in:
Patrick
2026-05-01 19:29:02 +02:00
commit 3344047d89
58 changed files with 3948 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
.idea/
*.iml
out/
srv/
build/
*.log
.gradle/
.DS_Store
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 WinniePatGG
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+39
View File
@@ -0,0 +1,39 @@
# SMPPlugin
1.21.4 Plugin for my SMP Server. This plugin is hardcoded for my Server. If you want to use it, go ahead. You'll need to change some stuff in the code
## 💡 Features
- Black Market
- Block Elevators (Currently broken)
- Bloodmoon (Stronger, Faster mobs)
- Polls
- Player reports (With db and gui)
- Starter (To start the Event)
- Suggestions (With db and gui)
- waypoints (Commands)
- trash (Delete Items)
- Coding Tag (/coding on) Requires a Resourcepack (Maybe I'll provide it)
## 🚀 Getting Started
### Prerequisites
- Java 21
- Gradle
- Paper 1.21.4 Server
- (Not needed but recommended) Resourcepack
### Build & Run
```bash
# Clone the repository
git clone https://github.com/WinniePatGG/SMPPlugin.git
# Navigate into the project folder
cd SMPPlugin
# Build the plugin (example using Gradle)
./gradlew build
```
Made with ❤️ by WinniePatGG.
+70
View File
@@ -0,0 +1,70 @@
plugins {
id 'java'
id("xyz.jpenilla.run-paper") version "2.3.1"
}
group = 'de.winniepat'
version = '1.0'
repositories {
mavenCentral()
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
maven { url = 'https://jitpack.io' }
maven { url 'https://repo.lucko.me' }
}
dependencies {
compileOnly('io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT')
implementation 'org.xerial:sqlite-jdbc:3.42.0.0'
compileOnly('com.github.MilkBowl:VaultAPI:1.7') {
exclude group: 'org.bukkit', module: 'bukkit'
}
compileOnly 'net.luckperms:api:5.4'
}
tasks {
runServer {
minecraftVersion("1.21")
}
}
def targetJavaVersion = 21
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release.set(targetJavaVersion)
}
}
processResources {
def props = [version: version]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(sourceSets.main.resources.srcDirs) {
include '**/*.yml'
}
}
tasks.register('copyPlugin', Copy) {
dependsOn build
from("$buildDir/libs")
include('*.jar')
into("$rootDir/srv/plugins")
}
build.finalizedBy(copyPlugin)
View File
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+249
View File
@@ -0,0 +1,249 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+92
View File
@@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+1
View File
@@ -0,0 +1 @@
rootProject.name = 'SMPPlugin'
@@ -0,0 +1,238 @@
package de.winniepat.SMPPlugin;
import de.winniepat.SMPPlugin.blackmarket.*;
import de.winniepat.SMPPlugin.blockelevator.*;
import de.winniepat.SMPPlugin.bloodmoon.*;
import de.winniepat.SMPPlugin.commands.*;
import de.winniepat.SMPPlugin.listeners.*;
import de.winniepat.SMPPlugin.polls.*;
import de.winniepat.SMPPlugin.report.*;
import de.winniepat.SMPPlugin.report.commands.*;
import de.winniepat.SMPPlugin.starter.MoveEvent;
import de.winniepat.SMPPlugin.starter.StartCommand;
import de.winniepat.SMPPlugin.suggestions.*;
import de.winniepat.SMPPlugin.suggestions.commands.*;
import de.winniepat.SMPPlugin.waypoints.*;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.command.*;
import org.bukkit.configuration.file.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.sql.SQLException;
import java.util.*;
public final class SMPPlugin extends JavaPlugin {
private static SMPPlugin instance;
private WaypointsDatabase waypointsDatabase;
private ReportDatabase reportDatabase;
private SuggestionManager suggestionManager;
CodingCommand codingCommand = new CodingCommand(this, this);
private FileConfiguration messages;
private BloodmoonManager bloodmoonManager;
public static Set<UUID> frozenPlayers = new HashSet<>();
@Override
public void onEnable() {
saveDefaultMessages();
loadMessages();
instance = this;
File dbFile = new File(getDataFolder(), "suggestions.db");
try {
this.suggestionManager = new SuggestionManager(dbFile);
} catch (SQLException e) {
throw new RuntimeException(e);
}
registerWaypoints();
getLogger().info("Waypoints registered");
registerReportDatabase();
getLogger().info("Reports registered");
registerBloodMoon();
getLogger().info("BloodMoon registered");
registerCommands();
getLogger().info("Commands registered");
registerListeners();
getLogger().info("Listener registered");
registerPollsystem();
getLogger().info("PollSystem registered");
registerSuggestionDatabase();
getLogger().info("Suggestion Database registered");
}
@Override
public void onDisable() {
getLogger().warning("Ach Leck Eier!");
bloodmoonManager.endBloodmoon();
}
private void registerCommands() {
Objects.requireNonNull(getCommand("trash")).setExecutor(new TrashCommand(this, this));
Objects.requireNonNull(getCommand("report")).setExecutor(new ReportCommand(reportDatabase, this));
Objects.requireNonNull(getCommand("reports")).setExecutor(new ReportsCommand(this, reportDatabase, this));
Objects.requireNonNull(getCommand("suggestion")).setExecutor(new SuggestionCommand(suggestionManager, this, this));
Objects.requireNonNull(getCommand("suggestions")).setExecutor(new SuggestionListCommand(suggestionManager));
Objects.requireNonNull(getCommand("suggestionstatus")).setExecutor(new SuggestionStatusCommand(suggestionManager, this));
Objects.requireNonNull(getCommand("coding")).setExecutor(new CodingCommand(this, this));
Objects.requireNonNull(getCommand("suggestionsgui")).setExecutor(this::handleGuiCommand);
Objects.requireNonNull(getCommand("bloodmoon")).setExecutor(new BloodmoonCommand(bloodmoonManager));
Objects.requireNonNull(getCommand("blackmarket")).setExecutor(new BlackMarketCommand(this));
Objects.requireNonNull(getCommand("getwaypoint")).setExecutor(new WaypointCommands(waypointsDatabase, this));
Objects.requireNonNull(getCommand("addwaypoint")).setExecutor(new WaypointCommands(waypointsDatabase, this));
Objects.requireNonNull(getCommand("smp")).setExecutor(new StartCommand());
Objects.requireNonNull(getCommand("spawn")).setExecutor(new SpawnCommand());
Objects.requireNonNull(getCommand("shop")).setExecutor(new ShopCommand());
}
private boolean handleGuiCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player player) {
new SuggestionGui(suggestionManager, this).open(player, null);
} else {
sender.sendMessage("§cOnly players can open the GUI.");
}
return true;
}
private void registerListeners() {
getServer().getPluginManager().registerEvents(new CodingCleanupListener(codingCommand), this);
getServer().getPluginManager().registerEvents(new BlackMarketEffectListener(this), this);
getServer().getPluginManager().registerEvents(new WaypointListener(waypointsDatabase, this), this);
getServer().getPluginManager().registerEvents(new PlayerJumpListener(), this);
getServer().getPluginManager().registerEvents(new PlayerSneakListener(), this);
getServer().getPluginManager().registerEvents(new MoveEvent(), this);
getServer().getPluginManager().registerEvents(new QuitLightningListener(), this);
Bukkit.getPluginManager().registerEvents(new SuggestionGui(suggestionManager, this), this);
Bukkit.getPluginManager().registerEvents(new FirstPlayerJoinTracker(this), this);
}
private void registerPollsystem() {
File pollDataFile = new File(getDataFolder(), "polls.yml");
PollManager pollManager = new PollManager(pollDataFile);
if (!pollDataFile.getParentFile().exists()) {
pollDataFile.getParentFile().mkdirs();
}
Objects.requireNonNull(getCommand("pollstart")).setExecutor(new PollCommand(pollManager, this));
Objects.requireNonNull(getCommand("pollvote")).setExecutor((sender, cmd, label, args) -> {
if (sender instanceof Player player) {
new PollVoteGUI(pollManager, this).openVoteGUI(player);
}
return true;
});
Objects.requireNonNull(getCommand("pollresults")).setExecutor((sender, cmd, label, args) -> {
if (sender instanceof Player player && player.hasPermission("poll.view")) {
new PollResultGUI(pollManager).openResultsGUI(player);
}
return true;
});
getServer().getPluginManager().registerEvents(new PollVoteGUI(pollManager, this), this);
}
private void registerSuggestionDatabase() {
if (!getDataFolder().exists()) {
getDataFolder().mkdirs();
}
File dbFile = new File(getDataFolder(), "suggestions.db");
try {
suggestionManager = new SuggestionManager(dbFile);
} catch (SQLException e) {
getLogger().severe("Couldn't initialize the Suggestion Database.");
e.printStackTrace();
return;
}
getLogger().info("Suggestion Database connected successfully");
}
private void saveDefaultMessages() {
if (!getDataFolder().exists()) {
getDataFolder().mkdirs();
}
File file = new File(getDataFolder(), "messages.yml");
if (!file.exists()) {
saveResource("messages.yml", false);
}
}
private void loadMessages() {
File file = new File(getDataFolder(), "messages.yml");
messages = YamlConfiguration.loadConfiguration(file);
Bukkit.getLogger().info("[DEBUG] Loaded keys: " + messages.getKeys(true));
}
public String getMessage(String key, Map<String, String> placeholders) {
if (!messages.contains(key)) {
getLogger().warning("Missing message key: " + key);
getLogger().warning("Available keys: " + messages.getKeys(true));
return ChatColor.RED + "Message not found: " + key;
}
String msg = messages.getString(key);
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
msg = msg.replace("%" + entry.getKey() + "%", entry.getValue());
}
return ChatColor.translateAlternateColorCodes('&', msg);
}
private void checkForElevator(Player player) {
Block blockBelow = player.getLocation().subtract(0, 0.1, 0).getBlock();
if (blockBelow.getType() != Material.LIGHT_WEIGHTED_PRESSURE_PLATE) return;
// Handle sneaking (go down)
if (player.isSneaking() && !CooldownManager.isOnCooldown(player.getUniqueId())) {
for (int y = blockBelow.getY() - 1; y >= 0; y--) {
Block below = player.getWorld().getBlockAt(blockBelow.getX(), y, blockBelow.getZ());
if (below.getType() == Material.LIGHT_WEIGHTED_PRESSURE_PLATE) {
player.teleport(new Location(player.getWorld(), below.getX() + 0.5, y + 1, below.getZ() + 0.5));
player.playSound(player.getLocation(), Sound.BLOCK_PISTON_CONTRACT, 1f, 1f);
player.getWorld().spawnParticle(Particle.CLOUD, player.getLocation(), 20, 0.3, 0.3, 0.3);
CooldownManager.setCooldown(player.getUniqueId());
return;
}
}
player.sendMessage(ChatColor.YELLOW + "No elevator block below!");
CooldownManager.setCooldown(player.getUniqueId());
}
if (player.getVelocity().getY() > 0.2 && !CooldownManager.isOnCooldown(player.getUniqueId())) {
for (int y = blockBelow.getY() + 2; y <= player.getWorld().getMaxHeight(); y++) {
Block above = player.getWorld().getBlockAt(blockBelow.getX(), y, blockBelow.getZ());
if (above.getType() == Material.LIGHT_WEIGHTED_PRESSURE_PLATE) {
player.teleport(new Location(player.getWorld(), above.getX() + 0.5, y + 1, above.getZ() + 0.5));
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f);
player.getWorld().spawnParticle(Particle.PORTAL, player.getLocation(), 30, 0.5, 1, 0.5);
CooldownManager.setCooldown(player.getUniqueId());
return;
}
}
player.sendMessage(ChatColor.YELLOW + "No elevator block above!");
CooldownManager.setCooldown(player.getUniqueId());
}
}
private void registerBloodMoon() {
this.bloodmoonManager = new BloodmoonManager(this);
getServer().getPluginManager().registerEvents(new BloodmoonListener(bloodmoonManager, this), this);
new BloodmoonTask(this, bloodmoonManager).start();
}
private void registerWaypoints() {
reportDatabase = new ReportDatabase(this);
}
private void registerReportDatabase() {
reportDatabase = new ReportDatabase(this);
}
public static SMPPlugin getInstance() {
return instance;
}
}
@@ -0,0 +1,29 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
public class BlackMarketCommand implements CommandExecutor {
private final BlackMarketManager manager;
public BlackMarketCommand(JavaPlugin plugin) {
this.manager = new BlackMarketManager(plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("This command can only be used by players.");
return true;
}
manager.spawnMarket();
player.sendMessage(ChatColor.DARK_PURPLE + "You have summoned the Black Market.");
return true;
}
}
@@ -0,0 +1,132 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.*;
import org.bukkit.event.player.PlayerExpChangeEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class BlackMarketEffectListener implements Listener {
private final JavaPlugin plugin;
private final Random random = new Random();
private final NamespacedKey catchKey;
public BlackMarketEffectListener(JavaPlugin plugin) {
this.plugin = plugin;
this.catchKey = new NamespacedKey(plugin, "blackmarket_catch");
new BukkitRunnable() {
@Override
public void run() {
for (Player p : Bukkit.getOnlinePlayers()) {
checkCatch(p, EquipmentSlot.HEAD, "hunger_drain", () -> {
if (p.getFoodLevel() > 1) p.setFoodLevel(p.getFoodLevel() - 1);
});
checkCatch(p, EquipmentSlot.HAND, "burn_in_day", () -> {
if (isDaylight(p)) p.setFireTicks(40);
});
checkCatch(p, EquipmentSlot.FEET, "random_blindness", () -> {
if (random.nextInt(100) < 5)
p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 0));
});
}
}
}.runTaskTimer(plugin, 0L, 40L);
}
@EventHandler
public void onEntityDamage(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Player p)) return;
ItemStack item = p.getInventory().getItemInMainHand();
String catchId = getCatch(item);
if (catchId == null) return;
switch (catchId) {
case "self_damage" -> {
p.damage(2);
p.sendMessage("§cThe weapon wounds you!");
}
case "unstable" -> {
if (random.nextInt(100) < 10)
p.getWorld().createExplosion(p.getLocation(), 2f, false, false, p);
}
case "lightning_touch" -> {
Entity target = event.getEntity();
target.getWorld().strikeLightningEffect(target.getLocation());
}
case "hurt_nearby_allies" -> {
for (Player nearby : p.getWorld().getPlayers()) {
if (!nearby.equals(p) && nearby.getLocation().distance(p.getLocation()) < 5)
nearby.damage(2);
}
}
case "levitate_on_crit" -> {
if (p.getAttackCooldown() > 0.9f)
p.addPotionEffect(new PotionEffect(PotionEffectType.LEVITATION, 40, 1));
}
case "arrow_bounceback" -> {
if (p.getInventory().getItemInMainHand().getType().toString().endsWith("BOW") && random.nextInt(100) < 20) {
p.damage(2);
p.sendMessage(ChatColor.DARK_RED + "Your cursed arrow rebounds!");
}
}
}
}
@EventHandler
public void onHitTaken(EntityDamageEvent event) {
if (!(event.getEntity() instanceof Player p)) return;
checkCatch(p, EquipmentSlot.CHEST, "slowness_when_hit", () -> {
p.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, 60, 2));
});
checkCatch(p, EquipmentSlot.FEET, "more_knockback", () -> {
p.setVelocity(p.getVelocity().multiply(2));
});
}
@EventHandler
public void onXpGain(PlayerExpChangeEvent event) {
Player p = event.getPlayer();
checkCatch(p, EquipmentSlot.HEAD, "xp_boost", () -> {
event.setAmount(event.getAmount() * 2);
});
}
private void checkCatch(Player p, EquipmentSlot slot, String catchId, Runnable action) {
ItemStack item = p.getInventory().getItem(slot);
if (item != null && item.hasItemMeta()) {
ItemMeta meta = item.getItemMeta();
if (meta.getPersistentDataContainer().has(catchKey, PersistentDataType.STRING)) {
String tag = meta.getPersistentDataContainer().get(catchKey, PersistentDataType.STRING);
if (tag != null && tag.equals(catchId)) {
action.run();
}
}
}
}
private String getCatch(ItemStack item) {
if (item == null || !item.hasItemMeta()) return null;
ItemMeta meta = item.getItemMeta();
if (!meta.getPersistentDataContainer().has(catchKey, PersistentDataType.STRING)) return null;
return meta.getPersistentDataContainer().get(catchKey, PersistentDataType.STRING);
}
private boolean isDaylight(Player player) {
long time = player.getWorld().getTime();
return time < 12300 || time > 23850;
}
}
@@ -0,0 +1,163 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.NamespacedKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BlackMarketItems {
public static List<BlackMarketOffer> getRandomOffers(JavaPlugin plugin, int count) {
List<BlackMarketOffer> all = new ArrayList<>();
all.add(bloodBlade(plugin));
all.add(xpHood(plugin));
all.add(vampireAxe(plugin));
all.add(swiftBoots(plugin));
all.add(witherBow(plugin));
all.add(unstableBlade(plugin));
all.add(lightningAxe(plugin));
all.add(bounceBow(plugin));
all.add(levitateChest(plugin));
Collections.shuffle(all);
return all.subList(0, Math.min(count, all.size()));
}
private static BlackMarketOffer bloodBlade(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_SWORD);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_RED + "Blood Blade");
meta.addEnchant(Enchantment.SHARPNESS, 5, true);
meta.addEnchant(Enchantment.FIRE_ASPECT, 2, true);
meta.setLore(List.of("§cDeals devastating damage.", "§7But harms the wielder on hit."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "self_damage");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.NETHERITE_INGOT, 3),
new ItemStack(Material.ROTTEN_FLESH, 64)
), "self_damage");
}
private static BlackMarketOffer xpHood(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_HELMET);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GOLD + "Hood of Hunger");
meta.addEnchant(Enchantment.THORNS, 3, true);
meta.setLore(List.of("§eBoosts XP gain.", "§7But constantly drains hunger."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "hunger_drain");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.EXPERIENCE_BOTTLE, 32)
), "hunger_drain");
}
private static BlackMarketOffer vampireAxe(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_AXE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_PURPLE + "Vampire Axe");
meta.addEnchant(Enchantment.SHARPNESS, 5, true);
meta.setLore(List.of("§cHeals on hit.", "§7Burns in sunlight."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "burn_in_day");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.EMERALD, 20)
), "burn_in_day");
}
private static BlackMarketOffer swiftBoots(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_BOOTS);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.AQUA + "Boots of Swiftness");
meta.addEnchant(Enchantment.FEATHER_FALLING, 4, true);
meta.setLore(List.of("§bIncreases speed.", "§7Causes knockback vulnerability."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "more_knockback");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.EMERALD, 15)
), "more_knockback");
}
private static BlackMarketOffer witherBow(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.BOW);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_GRAY + "Withering Bow");
meta.addEnchant(Enchantment.POWER, 5, true);
meta.setLore(List.of("§7Applies wither.", "§cHurts allies nearby."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "hurt_nearby_allies");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.EMERALD, 25)
), "hurt_nearby_allies");
}
private static BlackMarketOffer unstableBlade(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_PURPLE + "Blade of Instability");
meta.addEnchant(Enchantment.SHARPNESS, 6, true);
meta.setLore(List.of("§5Massive power.", "§cMight explode on hit."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "unstable");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.NETHERITE_INGOT, 2),
new ItemStack(Material.TNT, 16)
), "unstable");
}
private static BlackMarketOffer lightningAxe(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_AXE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GOLD + "Lightning Touch Axe");
meta.addEnchant(Enchantment.SHARPNESS, 5, true);
meta.setLore(List.of("§eElectrocutes enemies.", "§7Risk of chain lightning."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "lightning_touch");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.GOLD_BLOCK, 4)
), "lightning_touch");
}
private static BlackMarketOffer bounceBow(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.BOW);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GRAY + "Rebounding Bow");
meta.addEnchant(Enchantment.POWER, 4, true);
meta.addEnchant(Enchantment.PUNCH, 1, true);
meta.setLore(List.of("§7Sometimes bounces arrows back... on YOU."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "arrow_bounceback");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.SPECTRAL_ARROW, 64)
), "arrow_bounceback");
}
private static BlackMarketOffer levitateChest(JavaPlugin plugin) {
ItemStack item = new ItemStack(Material.NETHERITE_CHESTPLATE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_AQUA + "Wings of the Void");
meta.addEnchant(Enchantment.PROTECTION, 4, true);
meta.setLore(List.of("§3Grants levitation on crit.", "§8Leaves you floating."));
meta.getPersistentDataContainer().set(new NamespacedKey(plugin, "blackmarket_catch"),
PersistentDataType.STRING, "levitate_on_crit");
item.setItemMeta(meta);
return new BlackMarketOffer(item, List.of(
new ItemStack(Material.ENDER_PEARL, 16),
new ItemStack(Material.ELYTRA, 1)
), "levitate_on_crit");
}
}
@@ -0,0 +1,93 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class BlackMarketManager {
private final JavaPlugin plugin;
public BlackMarketManager(JavaPlugin plugin) {
this.plugin = plugin;
}
public void spawnMarket() {
Location spawnLoc = getRandomSpawnLocation();
Villager trader = spawnBlackMarketVillager(spawnLoc);
Bukkit.broadcastMessage(ChatColor.GRAY + "☠ A mysterious trader has appeared nearby...");
String coords = String.format("X: %d, Y: %d, Z: %d",
spawnLoc.getBlockX(), spawnLoc.getBlockY(), spawnLoc.getBlockZ());
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.getLocation().distance(spawnLoc) <= 200) {
player.sendMessage(ChatColor.GRAY + "§7The trader lurks around §f" + coords);
}
}
new BukkitRunnable() {
@Override
public void run() {
if (trader == null || trader.isDead() || !trader.isValid()) {
cancel();
return;
}
trader.getWorld().spawnParticle(
Particle.ENCHANT,
trader.getLocation().add(0, 2, 0),
20,
0.5, 0.5, 0.5,
0.1
);
}
}.runTaskTimer(plugin, 0L, 20L);
new BukkitRunnable() {
@Override
public void run() {
if (trader != null && trader.isValid()) {
trader.remove();
Bukkit.broadcastMessage(ChatColor.GRAY + "The Black Market fades into the shadows...");
}
}
}.runTaskLater(plugin, 20 * 60 * 5);
}
private Villager spawnBlackMarketVillager(Location loc) {
Villager v = loc.getWorld().spawn(loc, Villager.class);
v.setAI(false);
v.setInvulnerable(true);
v.setCustomName(ChatColor.DARK_PURPLE + "Black Market");
v.setCustomNameVisible(true);
v.setProfession(Villager.Profession.NITWIT);
v.setVillagerLevel(5);
v.setVillagerExperience(1);
v.setCanPickupItems(false);
BlackMarketTradeHandler.applyTrades(v, plugin);
return v;
}
private Location getRandomSpawnLocation() {
World world = Bukkit.getWorlds().get(0);
Random random = new Random();
int baseX = -635;
int baseZ = -837;
int radius = 100;
int x = baseX + random.nextInt(radius * 2 + 1) - radius;
int z = baseZ + random.nextInt(radius * 2 + 1) - radius;
int y = world.getHighestBlockYAt(x, z) + 1;
return new Location(world, x + 0.5, y, z + 0.5);
}
}
@@ -0,0 +1,17 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class BlackMarketOffer {
public final ItemStack item;
public final List<ItemStack> price;
public final String catchId;
public BlackMarketOffer(ItemStack item, List<ItemStack> price, String catchId) {
this.item = item;
this.price = price;
this.catchId = catchId;
}
}
@@ -0,0 +1,30 @@
package de.winniepat.SMPPlugin.blackmarket;
import org.bukkit.entity.Villager;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.ArrayList;
import java.util.List;
public class BlackMarketTradeHandler {
public static void applyTrades(Villager v, JavaPlugin plugin) {
List<MerchantRecipe> recipes = new ArrayList<>();
for (BlackMarketOffer offer : BlackMarketItems.getRandomOffers(plugin, 3)) {
MerchantRecipe recipe = new MerchantRecipe(offer.item, 9999);
recipe.setUses(0);
recipe.setExperienceReward(false);
for (ItemStack ingredient : offer.price) {
recipe.addIngredient(ingredient);
}
recipes.add(recipe);
}
v.setRecipes(recipes);
}
}
@@ -0,0 +1,19 @@
package de.winniepat.SMPPlugin.blockelevator;
import java.util.HashMap;
import java.util.UUID;
public class CooldownManager {
private static final HashMap<UUID, Long> cooldowns = new HashMap<>();
private static final long COOLDOWN_TIME_MS = 2;
public static boolean isOnCooldown(UUID player) {
if (!cooldowns.containsKey(player)) return false;
long lastUsed = cooldowns.get(player);
return (System.currentTimeMillis() - lastUsed) < COOLDOWN_TIME_MS;
}
public static void setCooldown(UUID player) {
cooldowns.put(player, System.currentTimeMillis());
}
}
@@ -0,0 +1,46 @@
package de.winniepat.SMPPlugin.blockelevator;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.metadata.FixedMetadataValue;
public class PlayerJumpListener implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
// Check if player moves upwards
if (event.getFrom().getY() < event.getTo().getY()) {
// Always check the block below the player's feet
Block blockBelow = player.getLocation().subtract(0, 0.1, 0).getBlock();
if (blockBelow.getType() == Material.CRYING_OBSIDIAN) {
if (!CooldownManager.isOnCooldown(player.getUniqueId())) {
for (int y = blockBelow.getY() + 2; y <= player.getWorld().getMaxHeight(); y++) {
Block above = player.getWorld().getBlockAt(blockBelow.getX(), y, blockBelow.getZ());
if (above.getType() == Material.CRYING_OBSIDIAN) {
player.teleport(new Location(player.getWorld(), above.getX() + 0.5, y + 1, above.getZ() + 0.5));
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f);
player.getWorld().spawnParticle(Particle.PORTAL, player.getLocation(), 30, 0.5, 1, 0.5);
CooldownManager.setCooldown(player.getUniqueId());
return;
}
}
player.sendMessage(ChatColor.YELLOW + "No elevator block above!");
} else if (!player.hasMetadata("jumpCooldownNotified")) {
player.sendMessage(ChatColor.RED + "Elevator is cooling down!");
player.setMetadata("jumpCooldownNotified", new FixedMetadataValue(SMPPlugin.getInstance(), true));
Bukkit.getScheduler().runTaskLater(SMPPlugin.getInstance(), () -> {
player.removeMetadata("jumpCooldownNotified", SMPPlugin.getInstance());
}, 20L);
}
}
}
}
}
@@ -0,0 +1,44 @@
package de.winniepat.SMPPlugin.blockelevator;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.metadata.FixedMetadataValue;
public class PlayerSneakListener implements Listener {
@EventHandler
public void onPlayerSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return;
Player player = event.getPlayer();
// Always check the block **below** the player's feet
Block blockBelow = player.getLocation().subtract(0, 0.1, 0).getBlock();
if (blockBelow.getType() == Material.CRYING_OBSIDIAN) {
if (!CooldownManager.isOnCooldown(player.getUniqueId())) {
for (int y = blockBelow.getY() - 1; y >= 0; y--) {
Block below = player.getWorld().getBlockAt(blockBelow.getX(), y, blockBelow.getZ());
if (below.getType() == Material.CRYING_OBSIDIAN) {
player.teleport(new Location(player.getWorld(), below.getX() + 0.5, y + 1, below.getZ() + 0.5));
player.playSound(player.getLocation(), Sound.BLOCK_PISTON_CONTRACT, 1f, 1f);
player.getWorld().spawnParticle(Particle.CLOUD, player.getLocation(), 20, 0.3, 0.3, 0.3);
CooldownManager.setCooldown(player.getUniqueId());
return;
}
}
player.sendMessage(ChatColor.YELLOW + "No elevator block below!");
} else if (!player.hasMetadata("sneakCooldownNotified")) {
player.sendMessage(ChatColor.RED + "Elevator is cooling down!");
player.setMetadata("sneakCooldownNotified", new FixedMetadataValue(SMPPlugin.getInstance(), true));
Bukkit.getScheduler().runTaskLater(SMPPlugin.getInstance(), () -> {
player.removeMetadata("sneakCooldownNotified", SMPPlugin.getInstance());
}, 20L);
}
}
}
}
@@ -0,0 +1,34 @@
package de.winniepat.SMPPlugin.bloodmoon;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class BloodmoonCommand implements CommandExecutor {
private final BloodmoonManager manager;
public BloodmoonCommand(BloodmoonManager manager) {
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission("bloodmoon.toggle")) {
sender.sendMessage(ChatColor.RED + "You do not have permission to do that.");
return true;
}
if (manager.isActive()) {
manager.endBloodmoon();
sender.sendMessage(ChatColor.GRAY + "☀ Bloodmoon ended.");
} else {
manager.startBloodmoon();
sender.sendMessage(ChatColor.RED + "☠ Bloodmoon started.");
}
return true;
}
}
@@ -0,0 +1,160 @@
package de.winniepat.SMPPlugin.bloodmoon;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class BloodmoonListener implements Listener {
private final BloodmoonManager manager;
private final JavaPlugin plugin;
private final Random random = new Random();
public BloodmoonListener(BloodmoonManager manager, JavaPlugin plugin) {
this.manager = manager;
this.plugin = plugin;
}
@EventHandler
public void onBedEnter(PlayerBedEnterEvent event) {
if (manager.isActive()) {
event.setCancelled(true);
event.getPlayer().sendMessage(ChatColor.RED + "You cannot sleep during a Bloodmoon!");
}
}
@EventHandler
public void onMobSpawn(CreatureSpawnEvent event) {
if (!manager.isActive()) return;
LivingEntity entity = event.getEntity();
if (entity instanceof Zombie z && z.isBaby()) return;
if (random.nextInt(100) < 20) {
spawnBoss(entity);
return;
}
if (entity instanceof Creeper creeper) {
creeper.setExplosionRadius(6);
if (random.nextInt(100) < 30) {
creeper.setPowered(true);
}
}
if (entity.getAttribute(Attribute.MAX_HEALTH) != null) {
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(
entity.getAttribute(Attribute.MAX_HEALTH).getBaseValue() * 1.5
);
entity.setHealth(entity.getAttribute(Attribute.MAX_HEALTH).getBaseValue());
}
if (entity.getAttribute(Attribute.ATTACK_DAMAGE) != null) {
entity.getAttribute(Attribute.ATTACK_DAMAGE).setBaseValue(
entity.getAttribute(Attribute.ATTACK_DAMAGE).getBaseValue() + 2
);
}
if (entity.getAttribute(Attribute.MOVEMENT_SPEED) != null) {
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(
entity.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() * 1.2
);
}
}
private void spawnBoss(LivingEntity entity) {
if (!(entity instanceof Zombie || entity instanceof Skeleton || entity instanceof Husk || entity instanceof Drowned))
return;
entity.getPersistentDataContainer().set(
new NamespacedKey(plugin, "bloodmoon_boss"),
PersistentDataType.BYTE,
(byte) 1
);
entity.setCustomName(ChatColor.DARK_RED + "Blood Knight");
entity.setCustomNameVisible(true);
if (entity instanceof Zombie || entity instanceof Husk || entity instanceof Drowned) {
entity.getEquipment().setHelmet(createItem(Material.DIAMOND_HELMET, "Blood Helmet"));
entity.getEquipment().setChestplate(createItem(Material.DIAMOND_CHESTPLATE, "Blood Chestplate"));
entity.getEquipment().setLeggings(createItem(Material.DIAMOND_LEGGINGS, "Blood Leggings"));
entity.getEquipment().setBoots(createItem(Material.DIAMOND_BOOTS, "Blood Boots"));
entity.getEquipment().setItemInMainHand(createItem(Material.DIAMOND_SWORD, "Blood Blade"));
} else if (entity instanceof Skeleton) {
entity.getEquipment().setHelmet(createItem(Material.DIAMOND_HELMET, "Blood Helmet"));
entity.getEquipment().setChestplate(createItem(Material.NETHERITE_CHESTPLATE, "Blood Chestplate"));
entity.getEquipment().setItemInMainHand(createItem(Material.BOW, "Blood Bow"));
}
entity.getEquipment().setHelmetDropChance(0.001f);
entity.getEquipment().setChestplateDropChance(0.001f);
entity.getEquipment().setLeggingsDropChance(0.001f);
entity.getEquipment().setBootsDropChance(0.001f);
entity.getEquipment().setItemInMainHandDropChance(0.001f);
if (entity.getAttribute(Attribute.MAX_HEALTH) != null) {
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(80);
entity.setHealth(80);
}
if (entity.getAttribute(Attribute.MAX_HEALTH) != null) {
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(12);
}
if (entity.getAttribute(Attribute.MOVEMENT_SPEED) != null) {
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.35);
}
startParticleEffect(entity);
}
private ItemStack createItem(Material material, String name) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.RED + name);
meta.addEnchant(Enchantment.PROTECTION, 2, true);
meta.addEnchant(Enchantment.UNBREAKING, 3, true);
meta.addEnchant(Enchantment.SHARPNESS, 2, true);
item.setItemMeta(meta);
}
return item;
}
private void startParticleEffect(LivingEntity entity) {
new BukkitRunnable() {
int ticks = 0;
@Override
public void run() {
if (entity == null || entity.isDead() || !entity.isValid()) {
cancel();
return;
}
entity.getWorld().spawnParticle(
Particle.DUST,
entity.getLocation().add(0, 1, 0),
8,
0.3, 0.5, 0.3,
0,
new Particle.DustOptions(Color.RED, 1.5f)
);
if ((ticks += 5) > 20 * 300) cancel();
}
}.runTaskTimer(plugin, 0L, 5L);
}
}
@@ -0,0 +1,113 @@
package de.winniepat.SMPPlugin.bloodmoon;
import de.winniepat.SMPPlugin.blackmarket.BlackMarketManager;
import org.bukkit.*;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class BloodmoonManager {
private final JavaPlugin plugin;
private boolean active = false;
private BossBar bloodmoonBar;
private int durationTicks = 20 * 60 * 8;
private int ticksElapsed = 0;
private BukkitRunnable task;
public BloodmoonManager(JavaPlugin plugin) {
this.plugin = plugin;
}
public void startBloodmoon() {
if (active) return;
active = true;
Bukkit.broadcastMessage(ChatColor.DARK_RED + "☠ The Bloodmoon has risen... Be wary!");
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendTitle(ChatColor.RED + "Bloodmoon", ChatColor.DARK_RED + "Mobs are stronger tonight!", 10, 100, 20);
player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 20 * 60 * 8, 1, false, false, false));
player.playSound(player.getLocation(), Sound.ENTITY_WITHER_SPAWN, 1.0f, 1.0f);
player.spawnParticle(Particle.DUST, player.getLocation().add(0, 10, 0), 100,5, 5, 5, 0, new Particle.DustOptions(Color.RED, 2));
}
Bukkit.getScheduler().runTaskTimer(plugin, () -> {
for (Player player : Bukkit.getOnlinePlayers()) {
if (active) {
player.spawnParticle(Particle.DUST, player.getLocation().add(
Math.random() * 10 - 5, Math.random() * 5, Math.random() * 10 - 5),
5, 0, 0, 0, 0, new Particle.DustOptions(Color.RED, 1));
}
}
}, 0L, 1L);
bloodmoonBar = Bukkit.createBossBar("§4🌕 Bloodmoon Night", BarColor.RED, BarStyle.SEGMENTED_10);
bloodmoonBar.setProgress(1.0);
for (Player player : Bukkit.getOnlinePlayers()) {
bloodmoonBar.addPlayer(player);
}
task = new BukkitRunnable() {
@Override
public void run() {
ticksElapsed += 20;
double progress = Math.max(0, 1.0 - (double) ticksElapsed / durationTicks);
bloodmoonBar.setProgress(progress);
if (ticksElapsed >= durationTicks) {
endBloodmoon();
}
}
};
task.runTaskTimer(plugin, 20L, 20L);
}
public void endBloodmoon() {
if (!active) return;
active = false;
Bukkit.broadcastMessage(ChatColor.GRAY + "☀ The Bloodmoon has ended. It's safe again.");
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendTitle(ChatColor.YELLOW + "", ChatColor.GREEN + "Bloodmoon ended!", 10, 100, 20);
}
NamespacedKey key = new NamespacedKey(plugin, "bloodmoon_boss");
for (World world : Bukkit.getWorlds()) {
for (LivingEntity entity : world.getLivingEntities()) {
if (entity.getPersistentDataContainer().has(key, PersistentDataType.BYTE)) {
entity.getWorld().playSound(entity.getLocation(), Sound.ENTITY_WITHER_DEATH, 1f, 0.5f);
entity.remove();
}
}
}
if (new Random().nextInt(100) < 5) {
//new BlackMarketManager(plugin).spawnMarket();
}
if (task != null) {
task.cancel();
task = null;
}
if (bloodmoonBar != null) {
bloodmoonBar.removeAll();
bloodmoonBar = null;
}
ticksElapsed = 0;
}
public boolean isActive() {
return active;
}
}
@@ -0,0 +1,47 @@
package de.winniepat.SMPPlugin.bloodmoon;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class BloodmoonTask {
private final JavaPlugin plugin;
private final BloodmoonManager manager;
private boolean checkedThisNight = false;
public BloodmoonTask(JavaPlugin plugin, BloodmoonManager manager) {
this.plugin = plugin;
this.manager = manager;
}
public void start() {
new BukkitRunnable() {
@Override
public void run() {
World world = Bukkit.getWorlds().get(0);
long time = world.getTime();
if (time >= 13000 && time <= 13100) {
if (!checkedThisNight) {
checkedThisNight = true;
if (new Random().nextInt(100) < 5) {
manager.startBloodmoon();
}
}
}
if (time >= 0 && time < 1000) {
if (manager.isActive()) {
manager.endBloodmoon();
}
checkedThisNight = false;
}
}
}.runTaskTimer(plugin, 0L, 100L);
}
}
@@ -0,0 +1,131 @@
package de.winniepat.SMPPlugin.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.*;
import org.bukkit.command.*;
import org.bukkit.entity.*;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
public class CodingCommand implements CommandExecutor {
private Plugin plugin;
private SMPPlugin smpPlugin;
private final Map<UUID, ArmorStand> codingStands = new HashMap<>();
private final Map<UUID, BukkitRunnable> animationTasks = new HashMap<>();
private static final String[] COLORS = {
"#FF0000", "#FF4000", "#FF8000", "#FFBF00", "#FFFF00",
"#BFFF00", "#80FF00", "#40FF00", "#00FF00", "#00FF40",
"#00FF80", "#00FFBF", "#00FFFF", "#00BFFF", "#0080FF",
"#0040FF", "#0000FF", "#4000FF", "#8000FF", "#BF00FF",
"#FF00FF"
};
private final String[] frames = new String[21];
public CodingCommand(Plugin plugin, SMPPlugin smpPlugin) {
this.plugin = plugin;
this.smpPlugin = smpPlugin;
char[] glyphs = {
'\uE014', '\uE015', '\uE016', '\uE017', '\uE018',
'\uE019', '\uE020', '\uE021', '\uE022', '\uE023',
'\uE024', '\uE025', '\uE026', '\uE035', '\uE028',
'\uE029', '\uE030', '\uE031', '\uE032', '\uE033',
'\uE034'
};
for (int i = 0; i < 21; i++) {
frames[i] = glyphs[i] + " " + rgb("Coding...", COLORS[i]);
}
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) return false;
if (args.length != 1 || (!args[0].equalsIgnoreCase("on") && !args[0].equalsIgnoreCase("off"))) {
sender.sendMessage(smpPlugin.getMessage("command.coding.usage", Map.of("player", sender.getName())));
return true;
}
UUID uuid = player.getUniqueId();
if (args[0].equalsIgnoreCase("on")) {
if (codingStands.containsKey(uuid)) {
sender.sendMessage(smpPlugin.getMessage("command.coding.already", Map.of("player", sender.getName())));
return true;
}
Location loc = player.getLocation().add(0, 2.2, 0);
ArmorStand stand = player.getWorld().spawn(loc, ArmorStand.class);
stand.setVisible(false);
stand.setCustomNameVisible(true);
stand.setGravity(false);
stand.setMarker(true);
codingStands.put(uuid, stand);
BukkitRunnable task = new BukkitRunnable() {
int tick = 0;
@Override
public void run() {
if (!stand.isValid() || !player.isOnline()) {
this.cancel();
if (stand.isValid()) stand.remove();
codingStands.remove(uuid);
animationTasks.remove(uuid);
return;
}
stand.teleport(player.getLocation().add(0, 2.2, 0));
stand.setCustomName(frames[tick % frames.length]);
tick++;
}
};
task.runTaskTimer(plugin, 0L, 4L);
animationTasks.put(uuid, task);
sender.sendMessage(smpPlugin.getMessage("command.coding.marked", Map.of("player", sender.getName())));
}
if (args[0].equalsIgnoreCase("off")) {
if (animationTasks.containsKey(uuid)) {
animationTasks.get(uuid).cancel();
animationTasks.remove(uuid);
}
if (codingStands.containsKey(uuid)) {
ArmorStand stand = codingStands.remove(uuid);
if (stand != null && !stand.isDead()) {
stand.remove();
}
player.sendMessage("§aYou are no longer marked as coding.");
sender.sendMessage(smpPlugin.getMessage("command.coding.unmarked", Map.of("player", sender.getName())));
} else {
sender.sendMessage(smpPlugin.getMessage("command.coding.error", Map.of("player", sender.getName())));
}
}
return true;
}
private static String rgb(String text, String hexColor) {
StringBuilder out = new StringBuilder("§x");
for (char c : hexColor.substring(1).toCharArray()) {
out.append('§').append(c);
}
return out + text;
}
public Map<UUID, ArmorStand> getCodingStands() {
return codingStands;
}
public Map<UUID, BukkitRunnable> getAnimationTasks() {
return animationTasks;
}
}
@@ -0,0 +1,16 @@
package de.winniepat.SMPPlugin.commands;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ShopCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String @NotNull [] args) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"minecraft:tp " + sender.getName() + " -696 64 -898 0.0 -7.0");
return false;
}
}
@@ -0,0 +1,15 @@
package de.winniepat.SMPPlugin.commands;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class SpawnCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"minecraft:tp " + sender.getName() + " -635.5 68 -837.5 -90 0");
return false;
}
}
@@ -0,0 +1,45 @@
package de.winniepat.SMPPlugin.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Map;
public class TrashCommand implements CommandExecutor, Listener {
private final JavaPlugin plugin;
private final SMPPlugin smpPlugin;
public TrashCommand(JavaPlugin plugin, SMPPlugin smpPlugin) {
this.plugin = plugin;
this.smpPlugin = smpPlugin;
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player p)) {
sender.sendMessage(smpPlugin.getMessage("command.error.no_player", Map.of("player", sender.getName())));
return true;
}
Inventory trash = Bukkit.createInventory(p, 27, "§cTrash");
p.openInventory(trash);
return true;
}
@org.bukkit.event.EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
if (event.getView().getTitle().equals("§cTrash")) {
event.getInventory().clear();
}
}
}
@@ -0,0 +1,35 @@
package de.winniepat.SMPPlugin.listeners;
import de.winniepat.SMPPlugin.commands.CodingCommand;
import org.bukkit.entity.ArmorStand;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.UUID;
public class CodingCleanupListener implements Listener {
private final CodingCommand codingCommand;
public CodingCleanupListener(CodingCommand codingCommand) {
this.codingCommand = codingCommand;
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
if (codingCommand.getAnimationTasks().containsKey(uuid)) {
codingCommand.getAnimationTasks().get(uuid).cancel();
codingCommand.getAnimationTasks().remove(uuid);
}
if (codingCommand.getCodingStands().containsKey(uuid)) {
ArmorStand stand = codingCommand.getCodingStands().remove(uuid);
if (stand != null && !stand.isDead()) {
stand.remove();
}
}
}
}
@@ -0,0 +1,104 @@
package de.winniepat.SMPPlugin.listeners;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.*;
public class FirstPlayerJoinTracker implements Listener {
String longText = "Einst lebten alle friedlich beisammen in der Stadt Lunaris. " +
"Doch eines Abends, als der Blutmond über die Stadt zog, wurde Lunaris von unzähligen Monstern attackiert und fast vollständig zerstört. " +
"Die tapferen Bewohner versuchten mit aller Kraft der Attacke standzuhalten doch es war vergebens. Die Monster waren einfach zu stark, und es waren zu viele. " +
"Die Stadtbewohner konnten nichts ausrichten. Alles, was blieb, war das Rathaus am Marktplatz und ein paar Überreste der Häuser… " +
"Doch die Hoffnung besteht, dass die Stadt Lunaris bald wieder in vollem Glanz erstrahlen kann und genauso belebt wird, wie sie es einst war.";
private final JavaPlugin plugin;
private final File file;
private final Gson gson = new Gson();
private final Set<UUID> joinedPlayers = new HashSet<>();
public FirstPlayerJoinTracker(JavaPlugin plugin) {
this.plugin = plugin;
this.file = new File(plugin.getDataFolder(), "players.json");
load();
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
if (joinedPlayers.add(uuid)) {
ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
BookMeta bookMeta = (BookMeta) book.getItemMeta();
bookMeta.setAuthor("Unknown Stranger");
try {
bookMeta.getClass().getMethod("setTitle", String.class).invoke(bookMeta, "Willkommen in Lunaris");
} catch (Exception ignored) {
}
List<String> pages = splitIntoPages(longText, 250);
bookMeta.setPages(pages);
book.setItemMeta(bookMeta);
event.getPlayer().getInventory().addItem(book);
save();
}
}
public void load() {
if (!file.exists()) {
plugin.getDataFolder().mkdirs();
save();
return;
}
try (FileReader reader = new FileReader(file)) {
Set<String> loaded = gson.fromJson(reader, new TypeToken<Set<String>>() {}.getType());
if (loaded != null) {
for (String id : loaded) {
joinedPlayers.add(UUID.fromString(id));
}
}
} catch (Exception e) {
plugin.getLogger().warning("Failed to load players.json: " + e.getMessage());
}
}
public void save() {
try (FileWriter writer = new FileWriter(file)) {
Set<String> ids = new HashSet<>();
for (UUID uuid : joinedPlayers) {
ids.add(uuid.toString());
}
gson.toJson(ids, writer);
} catch (Exception e) {
plugin.getLogger().warning("Failed to save players.json: " + e.getMessage());
}
}
private List<String> splitIntoPages(String text, int maxLength) {
List<String> pages = new ArrayList<>();
int index = 0;
while (index < text.length()) {
int endIndex = Math.min(index + maxLength, text.length());
pages.add(text.substring(index, endIndex));
index = endIndex;
}
return pages;
}
}
@@ -0,0 +1,16 @@
package de.winniepat.SMPPlugin.listeners;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
public class QuitLightningListener implements Listener {
@EventHandler
public void playerQuitListener(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (player.isOp()) {
player.getWorld().strikeLightningEffect(player.getLocation());
}
}
}
@@ -0,0 +1,76 @@
package de.winniepat.SMPPlugin.polls;
import java.util.*;
public class Poll {
private final String question;
private final List<String> options;
private final Map<UUID, String> votes = new HashMap<>();
private final String creator;
private final long endTimeMillis;
public Poll(String question, List<String> options, String creator, long durationSeconds) {
this.question = question;
this.options = options;
this.creator = creator;
this.endTimeMillis = System.currentTimeMillis() + (durationSeconds * 1000);
}
public Poll(String question, List<String> options, String creator, long endTimeMillis, Map<String, Integer> savedVotes) {
this.question = question;
this.options = options;
this.creator = creator;
this.endTimeMillis = endTimeMillis;
for (Map.Entry<String, Integer> entry : savedVotes.entrySet()) {
for (int i = 0; i < entry.getValue(); i++) {
votes.put(UUID.randomUUID(), entry.getKey());
}
}
}
public String getQuestion() {
return question;
}
public List<String> getOptions() {
return options;
}
public String getCreator() {
return creator;
}
public long getEndTimeMillis() {
return endTimeMillis;
}
public boolean vote(UUID player, String option) {
if (!options.contains(option) || isExpired()) return false;
votes.put(player, option);
return true;
}
public Map<String, Integer> getResults() {
Map<String, Integer> results = new HashMap<>();
for (String option : options) {
results.put(option, 0);
}
for (String vote : votes.values()) {
results.put(vote, results.get(vote) + 1);
}
return results;
}
public boolean hasVoted(UUID player) {
return votes.containsKey(player);
}
public boolean isExpired() {
return System.currentTimeMillis() > endTimeMillis;
}
public long getRemainingTimeSeconds() {
long remaining = endTimeMillis - System.currentTimeMillis();
return Math.max(remaining / 1000, 0);
}
}
@@ -0,0 +1,69 @@
package de.winniepat.SMPPlugin.polls;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class PollCommand implements CommandExecutor {
private final PollManager manager;
private final SMPPlugin smpPlugin;
public PollCommand(PollManager manager, SMPPlugin smpPlugin) {
this.manager = manager;
this.smpPlugin = smpPlugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) {
if (args.length < 3) {
sender.sendMessage(smpPlugin.getMessage("command.poll.usage", Map.of("player", sender.getName())));
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(smpPlugin.getMessage("error.no_player", Map.of("player", sender.getName())));
return true;
}
if (!player.hasPermission("poll.start")) {
sender.sendMessage(smpPlugin.getMessage("command.error.no_permission", Map.of("player", sender.getName())));
return true;
}
if (manager.hasActivePoll()) {
sender.sendMessage(smpPlugin.getMessage("command.poll.already_active", Map.of("player", sender.getName())));
return true;
}
if (args[0].startsWith("\"") && args[0].endsWith("\"")) {
String question = args[0].substring(1, args[0].length() - 1);
long duration;
try {
duration = Long.parseLong(args[1]);
} catch (NumberFormatException e) {
sender.sendMessage(smpPlugin.getMessage("command.poll.invalid_time", Map.of("player", sender.getName())));
return true;
}
List<String> options = Arrays.asList(Arrays.copyOfRange(args, 2, args.length));
manager.startPoll(question, options, player.getName(), duration);
Bukkit.broadcastMessage("§6Poll §e" + question + " §7(started " + duration + "s)");
for (Player online : Bukkit.getOnlinePlayers()) {
online.sendTitle(
"§6New Poll!",
"§7Use §e/vote §7to vote | " + duration + " Seconds left",
10, 60, 10
);
}
return true;
} else {
sender.sendMessage(smpPlugin.getMessage("command.poll.usage", Map.of("player", sender.getName())));
return true;
}
}
}
@@ -0,0 +1,85 @@
package de.winniepat.SMPPlugin.polls;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class PollManager {
private Poll currentPoll = null;
private final File dataFile;
private final YamlConfiguration data;
private final List<Poll> expiredPolls = new ArrayList<>();
public PollManager(File dataFile) {
this.dataFile = dataFile;
this.data = YamlConfiguration.loadConfiguration(dataFile);
load();
}
public void startPoll(String question, List<String> options, String creator, long durationSeconds) {
if (currentPoll != null && currentPoll.isExpired()) {
expiredPolls.add(currentPoll);
}
this.currentPoll = new Poll(question, options, creator, durationSeconds);
save();
}
public Poll getCurrentPoll() {
return currentPoll;
}
public void clearPoll() {
if (currentPoll != null) {
expiredPolls.add(currentPoll);
}
this.currentPoll = null;
data.set("poll", null);
save();
}
public boolean hasActivePoll() {
return currentPoll != null && !currentPoll.isExpired();
}
public List<Poll> getExpiredPolls() {
return expiredPolls;
}
public void save() {
if (currentPoll == null) return;
data.set("poll.question", currentPoll.getQuestion());
data.set("poll.options", currentPoll.getOptions());
data.set("poll.creator", currentPoll.getCreator());
data.set("poll.endTime", currentPoll.getEndTimeMillis());
data.set("poll.results", currentPoll.getResults());
try {
data.save(dataFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public void load() {
if (!data.contains("poll")) return;
String question = data.getString("poll.question");
List<String> options = data.getStringList("poll.options");
String creator = data.getString("poll.creator");
long endTime = data.getLong("poll.endTime");
Map<String, Integer> results = new HashMap<>();
if (data.contains("poll.results")) {
for (String key : data.getConfigurationSection("poll.results").getKeys(false)) {
results.put(key, data.getInt("poll.results." + key));
}
}
long duration = (endTime - System.currentTimeMillis()) / 1000;
if (duration > 0) {
this.currentPoll = new Poll(question, options, creator, duration);
} else {
this.currentPoll = new Poll(question, options, creator, endTime, results);
this.expiredPolls.add(currentPoll);
this.currentPoll = null;
}
}
}
@@ -0,0 +1,52 @@
package de.winniepat.SMPPlugin.polls;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.List;
public class PollResultGUI {
private final PollManager manager;
public PollResultGUI(PollManager manager) {
this.manager = manager;
}
public void openResultsGUI(Player player) {
int size = 9 * 3;
Inventory inv = Bukkit.createInventory(null, size, "§6Poll-Results");
int slot = 0;
Poll current = manager.getCurrentPoll();
if (current != null) {
ItemStack item = new ItemStack(Material.WRITABLE_BOOK);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§aActive: " + current.getQuestion());
meta.setLore(current.getResults().entrySet().stream()
.map(e -> "§7" + e.getKey() + ": §e" + e.getValue())
.toList());
item.setItemMeta(meta);
inv.setItem(slot++, item);
}
List<Poll> pastPolls = manager.getExpiredPolls();
for (Poll past : pastPolls) {
ItemStack item = new ItemStack(Material.BOOK);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§7Ended: " + past.getQuestion());
meta.setLore(past.getResults().entrySet().stream()
.map(e -> "§7" + e.getKey() + ": §e" + e.getValue())
.toList());
item.setItemMeta(meta);
inv.setItem(slot++, item);
if (slot >= size) break;
}
player.openInventory(inv);
}
}
@@ -0,0 +1,69 @@
package de.winniepat.SMPPlugin.polls;
import com.google.gson.annotations.Since;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.List;
import java.util.Map;
public class PollVoteGUI implements Listener {
private final PollManager manager;
private final SMPPlugin smpPlugin;
public PollVoteGUI(PollManager manager, SMPPlugin smpPlugin) {
this.smpPlugin = smpPlugin;
this.manager = manager;
}
public void openVoteGUI(Player player) {
if (!manager.hasActivePoll()) {
player.sendMessage("§cNo active Poll.");
return;
}
Poll poll = manager.getCurrentPoll();
Inventory inv = Bukkit.createInventory(null, 9, "§6Vote: " + poll.getQuestion());
int i = 0;
for (String option : poll.getOptions()) {
ItemStack item = new ItemStack(Material.PAPER);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§b" + option);
meta.setLore(List.of("§7Remaining Time: §e" + poll.getRemainingTimeSeconds() + " Seconds"));
item.setItemMeta(meta);
inv.setItem(i++, item);
}
player.openInventory(inv);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (event.getView().getTitle().startsWith("§6Vote:")) {
event.setCancelled(true);
ItemStack clicked = event.getCurrentItem();
if (clicked == null || !clicked.hasItemMeta()) return;
String vote = clicked.getItemMeta().getDisplayName().replace("§b", "");
Poll poll = manager.getCurrentPoll();
if (poll != null && !poll.hasVoted(player.getUniqueId())) {
poll.vote(player.getUniqueId(), vote);
player.sendMessage("§aYour vote for §e" + vote + " §awas registered.");
player.closeInventory();
} else {
player.sendMessage(smpPlugin.getMessage("command.error.no_permission", Map.of("player", player.getName())));
}
}
}
}
@@ -0,0 +1,92 @@
package de.winniepat.SMPPlugin.report;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
import java.util.Map;
public class ReportActionGUI {
private final SMPPlugin smpPlugin;
public ReportActionGUI(SMPPlugin smpPlugin) {
this.smpPlugin = smpPlugin;
}
public static void open(JavaPlugin plugin, ReportDatabase db, Player p, String reportId, SMPPlugin smpPlugin) {
Map<String, String> report = db.getReportById(reportId);
if (report == null) {
p.sendMessage(smpPlugin.getMessage("command.reportactiongui.not_found", Map.of("player", p.getName())));
return;
}
Inventory gui = Bukkit.createInventory(p, 27, "§eActions for the report #" + reportId);
ItemStack info = new ItemStack(Material.BOOK);
ItemMeta im = info.getItemMeta();
im.setDisplayName("§bReport Details");
im.setLore(List.of(
"§7Victim: §f" + report.get("reported"),
"§7By: §a" + report.get("reporter"),
"§7Reason: §f" + report.get("reason"),
"§8ID: " + reportId
));
info.setItemMeta(im);
gui.setItem(11, info);
ItemStack delete = new ItemStack(Material.BARRIER);
ItemMeta dm = delete.getItemMeta();
dm.setDisplayName("§cReport löschen");
delete.setItemMeta(dm);
gui.setItem(13, delete);
ItemStack ban = new ItemStack(Material.IRON_SWORD);
ItemMeta bm = ban.getItemMeta();
bm.setDisplayName("§4Ban Victim");
bm.setLore(List.of("§7Reason: §cSecurity Ban"));
ban.setItemMeta(bm);
gui.setItem(15, ban);
p.openInventory(gui);
Bukkit.getPluginManager().registerEvents(new org.bukkit.event.Listener() {
@EventHandler
public void onClick(InventoryClickEvent e) {
if (!e.getView().getTitle().equals("§eActions for report #" + reportId)) return;
if (!(e.getWhoClicked() instanceof Player clicker)) return;
ItemStack clicked = e.getCurrentItem();
if (clicked == null || clicked.getType() == Material.AIR) return;
if (clicked.getType() == Material.BARRIER) {
db.deleteReportById(reportId);
clicker.closeInventory();
clicker.playSound(clicker.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 0.5f);
clicker.sendMessage(smpPlugin.getMessage("reportactiongui.successful_delete", Map.of("player", clicker.getName())));
HandlerList.unregisterAll(this);
}
if (clicked.getType() == Material.IRON_SWORD) {
String target = report.get("reported");
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
"ban " + target + " Security Ban");
clicker.closeInventory();
clicker.sendMessage("§cPlayer was permanently banned.");
clicker.sendMessage(smpPlugin.getMessage("reportactiongui.successful_ban", Map.of("player", clicker.getName())));
HandlerList.unregisterAll(this);
}
e.setCancelled(true);
}
}, plugin);
}
}
@@ -0,0 +1,115 @@
package de.winniepat.SMPPlugin.report;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.sql.*;
import java.util.*;
public class ReportDatabase {
private final JavaPlugin plugin;
private Connection connection;
public ReportDatabase(JavaPlugin plugin) {
this.plugin = plugin;
connect();
createTable();
}
private void connect() {
try {
File folder = plugin.getDataFolder();
if (!folder.exists()) folder.mkdirs();
String url = "jdbc:sqlite:" + folder.getAbsolutePath() + "/reports.db";
connection = DriverManager.getConnection(url);
plugin.getLogger().info("Report database successfully connected.");
} catch (SQLException e) {
plugin.getLogger().warning("Error while connecting to the database: " + e.getMessage());
}
}
private void createTable() {
if (connection == null) return;
try (Statement stmt = connection.createStatement()) {
stmt.executeUpdate("""
CREATE TABLE IF NOT EXISTS reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporter TEXT,
reported TEXT,
reason TEXT,
timestamp TEXT
);
""");
} catch (SQLException e) {
plugin.getLogger().warning("Error while crating the table: " + e.getMessage());
}
}
public void insertReport(String reporter, String reported, String reason) {
if (connection == null) return;
try (PreparedStatement ps = connection.prepareStatement(
"INSERT INTO reports (reporter, reported, reason, timestamp) VALUES (?, ?, ?, datetime('now'))")) {
ps.setString(1, reporter);
ps.setString(2, reported);
ps.setString(3, reason);
ps.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().warning("Error while saving the report: " + e.getMessage());
}
}
public List<Map<String, String>> getAllReports() {
List<Map<String, String>> reports = new ArrayList<>();
if (connection == null) return reports;
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM reports ORDER BY id DESC")) {
while (rs.next()) {
Map<String, String> entry = new HashMap<>();
entry.put("id", rs.getString("id"));
entry.put("reporter", rs.getString("reporter"));
entry.put("reported", rs.getString("reported"));
entry.put("reason", rs.getString("reason"));
entry.put("timestamp", rs.getString("timestamp"));
reports.add(entry);
}
} catch (SQLException e) {
plugin.getLogger().warning("Error while loading the reports: " + e.getMessage());
}
return reports;
}
public Map<String, String> getReportById(String id) {
if (connection == null) return null;
try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM reports WHERE id = ?")) {
ps.setString(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Map<String, String> r = new HashMap<>();
r.put("id", rs.getString("id"));
r.put("reporter", rs.getString("reporter"));
r.put("reported", rs.getString("reported"));
r.put("reason", rs.getString("reason"));
r.put("timestamp", rs.getString("timestamp"));
return r;
}
} catch (SQLException e) {
plugin.getLogger().warning("Fehler beim Laden Report ID " + id + ": " + e.getMessage());
}
return null;
}
public void deleteReportById(String id) {
if (connection == null) return;
try (PreparedStatement ps = connection.prepareStatement("DELETE FROM reports WHERE id = ?")) {
ps.setString(1, id);
ps.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().warning("Fehler beim Löschen Report ID " + id + ": " + e.getMessage());
}
}
}
@@ -0,0 +1,51 @@
package de.winniepat.SMPPlugin.report.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import de.winniepat.SMPPlugin.report.ReportDatabase;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class ReportCommand implements CommandExecutor {
private final ReportDatabase db;
private final SMPPlugin plugin;
public ReportCommand(ReportDatabase db, SMPPlugin plugin) {
this.db = db;
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String @NotNull [] args) {
if (!(sender instanceof Player p)) {
return true;
}
if (args.length < 2) {
p.sendMessage(plugin.getMessage("command.report.usage", Map.of("player", sender.getName())));
return true;
}
Player target = Bukkit.getPlayerExact(args[0]);
if (target == null || target == p) {
sender.sendMessage(plugin.getMessage("command.report.invalid_player", Map.of("player", sender.getName())));
return true;
}
String reason = String.join(" ", args).substring(args[0].length()).trim();
if (reason.length() < 4) {
sender.sendMessage(plugin.getMessage("command.report.reason", Map.of("player", sender.getName())));
return true;
}
db.insertReport(p.getName(), target.getName(), reason);
sender.sendMessage(plugin.getMessage("command.report.success", Map.of("player", sender.getName())));
return true;
}
}
@@ -0,0 +1,131 @@
package de.winniepat.SMPPlugin.report.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import de.winniepat.SMPPlugin.report.ReportActionGUI;
import de.winniepat.SMPPlugin.report.ReportDatabase;
import org.bukkit.*;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.event.*;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.*;
public class ReportsCommand implements CommandExecutor, Listener {
private final ReportDatabase db;
private final SMPPlugin smpPlugin;
private final JavaPlugin plugin;
private final Map<UUID, Integer> pageMap = new HashMap<>();
private final int REPORTS_PER_PAGE = 28;
public ReportsCommand(JavaPlugin plugin, ReportDatabase db, SMPPlugin smpPlugin) {
this.plugin = plugin;
this.smpPlugin = smpPlugin;
this.db = db;
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player p)) return true;
if (!p.hasPermission("smputils.reports")) {
sender.sendMessage(smpPlugin.getMessage("command.error.no_permission", Map.of("player", sender.getName())));
return true;
}
pageMap.put(p.getUniqueId(), 0);
openReportPage(p, 0);
return true;
}
private void openReportPage(Player p, int page) {
List<Map<String, String>> reports = db.getAllReports();
int totalPages = (int) Math.ceil((double) reports.size() / REPORTS_PER_PAGE);
if (page < 0 || page >= totalPages) page = 0;
Inventory inv = Bukkit.createInventory(p, 54, "§cAll Reports - Page " + (page + 1));
int start = page * REPORTS_PER_PAGE;
int end = Math.min(start + REPORTS_PER_PAGE, reports.size());
for (int i = 10, index = start; index < end; index++, i++) {
if ((i + 1) % 9 == 0) i += 2;
Map<String, String> report = reports.get(index);
ItemStack paper = new ItemStack(Material.PAPER);
ItemMeta meta = paper.getItemMeta();
meta.setDisplayName("§eVictim: §f" + report.get("reported"));
meta.setLore(List.of(
"§7By: §a" + report.get("reporter"),
"§7Reason: §f" + report.get("reason"),
"§8ID: " + report.get("id")
));
paper.setItemMeta(meta);
inv.setItem(i, paper);
}
if (page > 0) {
ItemStack prev = new ItemStack(Material.ARROW);
ItemMeta meta = prev.getItemMeta();
meta.setDisplayName("§a⬅ Back");
prev.setItemMeta(meta);
inv.setItem(45, prev);
}
if (page < totalPages - 1) {
ItemStack next = new ItemStack(Material.ARROW);
ItemMeta meta = next.getItemMeta();
meta.setDisplayName("§a➡ Next Page");
next.setItemMeta(meta);
inv.setItem(53, next);
}
p.openInventory(inv);
pageMap.put(p.getUniqueId(), page);
}
@EventHandler
public void onClick(InventoryClickEvent e) {
if (!(e.getWhoClicked() instanceof Player p)) return;
String title = e.getView().getTitle();
if (!title.startsWith("§cAll Reports - Page")) return;
e.setCancelled(true);
ItemStack item = e.getCurrentItem();
if (item == null || !item.hasItemMeta() || item.getType() == Material.AIR) return;
UUID uuid = p.getUniqueId();
int currentPage = pageMap.getOrDefault(uuid, 0);
String name = item.getItemMeta().getDisplayName();
if (name.equals("§a➡ Next Page")) {
openReportPage(p, currentPage + 1);
return;
}
if (name.equals("§a⬅ Back")) {
openReportPage(p, currentPage - 1);
return;
}
String idLine = item.getItemMeta().getLore().stream()
.filter(l -> l.startsWith("§8ID: "))
.findFirst().orElse(null);
if (idLine == null) return;
String reportId = idLine.replace("§8ID: ", "");
Bukkit.getScheduler().runTask(plugin, () -> ReportActionGUI.open(plugin, db, p, reportId, smpPlugin));
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent e) {
if (!(e.getWhoClicked() instanceof Player p)) return;
if (e.getView().getTitle().startsWith("§cAll Reports - Page")) {
e.setCancelled(true);
}
}
}
@@ -0,0 +1,74 @@
package de.winniepat.SMPPlugin.secrets;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.CommandBlock;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemRarity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
public class SecretButtonHandler implements Listener {
private static final String WORLD = "world";
private static final int A_X = -652, A_Y = 63, A_Z = -837;
private static final int B_X = 0, B_Y = 0, B_Z = 0;
@EventHandler
public void onButtonPress(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if (event.getClickedBlock() == null) return;
Block block = event.getClickedBlock();
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
if (block.getType() != Material.STONE_BUTTON) return;
String world = block.getWorld().getName();
int x = block.getX(), y = block.getY(), z = block.getZ();
if (!world.equals(WORLD)) return;
if (x == A_X && y == A_Y && z == A_Z) {
if (SecretsSQLite.hasClaimedSecret(uuid, "secretA")) {
player.sendMessage("§cYou've already claimed this Secret!");
return;
}
ItemStack secret1 = new ItemStack(Material.SPORE_BLOSSOM);
ItemMeta secret1meta = secret1.getItemMeta();
secret1meta.setItemName("Prinzessinnen-Enzian");
secret1meta.addEnchant(Enchantment.SMITE, 5, false);
secret1meta.addEnchant(Enchantment.LOOTING, 3, false);
secret1meta.addEnchant(Enchantment.UNBREAKING, 3, false);
secret1meta.addEnchant(Enchantment.MENDING, 1, false);
secret1meta.setRarity(ItemRarity.EPIC);
secret1.setItemMeta(secret1meta);
player.getInventory().addItem(new ItemStack(secret1));
SecretsSQLite.markSecretClaimed(uuid, "secretA");
player.sendMessage("§aYou claimed Secret myfcknmelody's secret!");
return;
}
if (x == B_X && y == B_Y && z == B_Z) {
if (SecretsSQLite.hasClaimedSecret(uuid, "secretB")) {
player.sendMessage("§cYou've already claimed Secret B!");
return;
}
player.getInventory().addItem(new ItemStack(Material.DIAMOND));
SecretsSQLite.markSecretClaimed(uuid, "secretB");
player.sendMessage("§bYou claimed Secret B!");
}
}
}
@@ -0,0 +1,63 @@
package de.winniepat.SMPPlugin.secrets;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.sql.*;
import java.util.UUID;
public class SecretsSQLite {
private static Connection connection;
public static void initDatabase(JavaPlugin plugin) {
try {
File dbFile = new File(plugin.getDataFolder(), "data.db");
dbFile.getParentFile().mkdirs();
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
try (Statement stmt = connection.createStatement()) {
stmt.execute("""
CREATE TABLE IF NOT EXISTS secrets (
uuid TEXT PRIMARY KEY,
secretA INTEGER DEFAULT 0,
secretB INTEGER DEFAULT 0
);
""");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static boolean hasClaimedSecret(UUID uuid, String secret) {
try (PreparedStatement stmt = connection.prepareStatement("SELECT " + secret + " FROM secrets WHERE uuid = ?")) {
stmt.setString(1, uuid.toString());
ResultSet rs = stmt.executeQuery();
return rs.next() && rs.getInt(secret) == 1;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static void markSecretClaimed(UUID uuid, String secret) {
try {
// First ensure row exists
try (PreparedStatement insert = connection.prepareStatement("INSERT OR IGNORE INTO secrets(uuid) VALUES (?)")) {
insert.setString(1, uuid.toString());
insert.executeUpdate();
}
try (PreparedStatement update = connection.prepareStatement("UPDATE secrets SET " + secret + " = 1 WHERE uuid = ?")) {
update.setString(1, uuid.toString());
update.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static boolean hasClaimedBoth(UUID uuid) {
return hasClaimedSecret(uuid, "secretA") && hasClaimedSecret(uuid, "secretB");
}
}
@@ -0,0 +1,17 @@
package de.winniepat.SMPPlugin.starter;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class MoveEvent implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (SMPPlugin.frozenPlayers.contains(event.getPlayer().getUniqueId())) {
if (!event.getFrom().toVector().equals(event.getTo().toVector())) {
event.setTo(event.getFrom());
}
}
}
}
@@ -0,0 +1,52 @@
package de.winniepat.SMPPlugin.starter;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
public class StartCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("start")) {
Location spawn = new Location(Bukkit.getWorlds().get(0), 0, Bukkit.getWorlds().get(0).getHighestBlockYAt(0, 0) + 1, 0);
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.isOp()) {
player.teleport(spawn);
SMPPlugin.frozenPlayers.add(player.getUniqueId());
}
}
new BukkitRunnable() {
int countdown = 3;
@Override
public void run() {
if (countdown > 0) {
Bukkit.broadcastMessage(ChatColor.YELLOW + "Starts in " + countdown + "...");
countdown--;
} else {
for (Player player : Bukkit.getOnlinePlayers()) {
SMPPlugin.frozenPlayers.remove(player.getUniqueId());
player.sendTitle(ChatColor.GREEN + "Have Fun", ChatColor.YELLOW + "-SMP-", 10, 40, 10);
}
cancel();
}
}
}.runTaskTimer(SMPPlugin.getInstance(), 0, 20);
return true;
}
sender.sendMessage(ChatColor.RED + "Usage: /smp start");
return true;
}
}
@@ -0,0 +1,33 @@
package de.winniepat.SMPPlugin.suggestions;
import java.time.Instant;
import java.util.UUID;
public class Suggestion {
private final int id;
private final UUID playerUuid;
private final String playerName;
private final String title;
private final String content;
private SuggestionStatus status;
private final Instant timestamp;
public Suggestion(int id, UUID playerUuid, String playerName, String title, String content) {
this.id = id;
this.playerUuid = playerUuid;
this.playerName = playerName;
this.title = title;
this.content = content;
this.status = SuggestionStatus.OPEN;
this.timestamp = Instant.now();
}
public int getId() { return id; }
public UUID getPlayerUuid() { return playerUuid; }
public String getPlayerName() { return playerName; }
public String getTitle() { return title; }
public String getContent() { return content; }
public SuggestionStatus getStatus() { return status; }
public void setStatus(SuggestionStatus status) { this.status = status; }
public Instant getTimestamp() { return timestamp; }
}
@@ -0,0 +1,157 @@
package de.winniepat.SMPPlugin.suggestions;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.*;
public class SuggestionGui implements Listener {
private final SuggestionManager manager;
private final Map<UUID, Integer> pageMap = new HashMap<>();
private final SMPPlugin smpPlugin;
public SuggestionGui(SuggestionManager manager, SMPPlugin smpPlugin) {
this.smpPlugin = smpPlugin;
this.manager = manager;
}
public void open(Player player, SuggestionStatus filter) {
open(player, filter, pageMap.getOrDefault(player.getUniqueId(), 0));
}
public void open(Player player, SuggestionStatus filter, int page) {
List<Suggestion> suggestions = new ArrayList<>(manager.getAllSuggestions());
if (filter != null) suggestions.removeIf(s -> s.getStatus() != filter);
int itemsPerPage = 45;
int totalPages = (int) Math.ceil(suggestions.size() / (double) itemsPerPage);
page = Math.max(0, Math.min(page, totalPages - 1));
pageMap.put(player.getUniqueId(), page);
Inventory inv = Bukkit.createInventory(null, 54, "§8Suggestions §7Page " + (page + 1));
int start = page * itemsPerPage;
int end = Math.min(start + itemsPerPage, suggestions.size());
List<Suggestion> pageItems = suggestions.subList(start, end);
int i = 0;
for (Suggestion s : pageItems) {
ItemStack paper = new ItemStack(Material.PAPER);
ItemMeta meta = paper.getItemMeta();
meta.setDisplayName("§e[#" + s.getId() + "] §f" + s.getTitle());
List<String> lore = new ArrayList<>();
lore.add("§7Von: §a" + s.getPlayerName());
lore.add("§7Status: §b" + s.getStatus().name());
lore.add("§8" + s.getContent());
lore.add("§7» §dShift-Leftclick: review");
lore.add("§7» §aLeftclick: accept");
lore.add("§7» §cRightclick: deny");
lore.add("§7» §eShift-Rightclick: delete");
meta.setLore(lore);
paper.setItemMeta(meta);
inv.setItem(i++, paper);
}
inv.setItem(45, createControlItem(Material.ARROW, "§7« Bacl", "page:prev"));
inv.setItem(46, createFilterItem(Material.GREEN_WOOL, "§aAccepted", SuggestionStatus.ACCEPTED));
inv.setItem(47, createFilterItem(Material.YELLOW_WOOL, "§eReview", SuggestionStatus.REVIEW));
inv.setItem(48, createFilterItem(Material.RED_WOOL, "§cDenied", SuggestionStatus.REJECTED));
inv.setItem(49, createFilterItem(Material.GRAY_WOOL, "§7Show All", SuggestionStatus.NONE));
inv.setItem(53, createControlItem(Material.ARROW, "§7Next Page »", "page:next"));
player.openInventory(inv);
}
private ItemStack createFilterItem(Material mat, String name, SuggestionStatus status) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
if (status != null) meta.setLore(Collections.singletonList("filter:" + status.name()));
item.setItemMeta(meta);
return item;
}
private ItemStack createControlItem(Material mat, String name, String tag) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
meta.setLore(Collections.singletonList(tag));
item.setItemMeta(meta);
return item;
}
@EventHandler
public void onClick(InventoryClickEvent e) {
if (!e.getView().getTitle().startsWith("§8Suggestions")) return;
e.setCancelled(true);
ItemStack clicked = e.getCurrentItem();
if (clicked == null || !clicked.hasItemMeta()) return;
Player p = (Player) e.getWhoClicked();
ItemMeta meta = clicked.getItemMeta();
String name = meta.getDisplayName();
List<String> lore = meta.getLore();
if (lore == null || lore.isEmpty()) return;
String tag = lore.get(0);
if (tag.startsWith("page:")) {
int currentPage = pageMap.getOrDefault(p.getUniqueId(), 0);
int newPage = tag.equals("page:next") ? currentPage + 1 : currentPage - 1;
open(p, null, newPage);
return;
}
if (tag.startsWith("filter:")) {
String statusName = tag.substring(7);
SuggestionStatus status = SuggestionStatus.valueOf(statusName);
open(p, status, 0);
return;
}
if (!name.startsWith("§e[#")) return;
int id;
try {
id = Integer.parseInt(name.split("#")[1].split("]")[0]);
} catch (Exception ex) {
p.sendMessage(smpPlugin.getMessage("suggestiongui.processing_error", Map.of("player", p.getName())));
return;
}
Suggestion suggestion = manager.getSuggestionById(id);
if (suggestion == null) {
p.sendMessage("§cSuggestion not found.");
return;
}
switch (e.getClick()) {
case LEFT -> {
manager.updateStatus(id, SuggestionStatus.ACCEPTED);
p.sendMessage("§aSuggestion §e#" + id + " §aaccepted.");
}
case RIGHT -> {
manager.updateStatus(id, SuggestionStatus.REJECTED);
p.sendMessage("§cSuggestion §e#" + id + " §cdenied.");
}
case SHIFT_RIGHT -> {
manager.deleteSuggestion(id);
p.sendMessage("§eSuggestion §7#" + id + " deleted.");
}
case SHIFT_LEFT -> {
manager.updateStatus(id, SuggestionStatus.REVIEW);
p.sendMessage("§aSuggestion §e#" + id + " §awill be reviewedd.");
}
}
open(p, null, pageMap.getOrDefault(p.getUniqueId(), 0));
}
}
@@ -0,0 +1,117 @@
package de.winniepat.SMPPlugin.suggestions;
import java.io.File;
import java.sql.*;
import java.time.Instant;
import java.util.*;
public class SuggestionManager {
private final Connection connection;
public SuggestionManager(File file) throws SQLException {
this.connection = DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath());
createTable();
}
private void createTable() throws SQLException {
String sql = """
CREATE TABLE IF NOT EXISTS suggestions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL,
name TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
status TEXT NOT NULL,
timestamp TEXT NOT NULL
)
""";
try (Statement stmt = connection.createStatement()) {
stmt.execute(sql);
}
}
public Suggestion addSuggestion(UUID uuid, String name, String title, String content) {
String sql = "INSERT INTO suggestions(uuid, name, title, content, status, timestamp) VALUES(?,?,?,?,?,?)";
try (PreparedStatement stmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, uuid.toString());
stmt.setString(2, name);
stmt.setString(3, title);
stmt.setString(4, content);
stmt.setString(5, SuggestionStatus.OPEN.name());
stmt.setString(6, Instant.now().toString());
stmt.executeUpdate();
ResultSet keys = stmt.getGeneratedKeys();
if (keys.next()) {
int id = keys.getInt(1);
return new Suggestion(id, uuid, name, title, content);
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public Collection<Suggestion> getAllSuggestions() {
List<Suggestion> list = new ArrayList<>();
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM suggestions")) {
while (rs.next()) {
int id = rs.getInt("id");
UUID uuid = UUID.fromString(rs.getString("uuid"));
String name = rs.getString("name");
String title = rs.getString("title");
String content = rs.getString("content");
SuggestionStatus status = SuggestionStatus.valueOf(rs.getString("status"));
Suggestion suggestion = new Suggestion(id, uuid, name, title, content);
suggestion.setStatus(status);
list.add(suggestion);
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public Suggestion getSuggestionById(int id) {
String sql = "SELECT * FROM suggestions WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
UUID uuid = UUID.fromString(rs.getString("uuid"));
String name = rs.getString("name");
String title = rs.getString("title");
String content = rs.getString("content");
SuggestionStatus status = SuggestionStatus.valueOf(rs.getString("status"));
Suggestion suggestion = new Suggestion(id, uuid, name, title, content);
suggestion.setStatus(status);
return suggestion;
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void updateStatus(int id, SuggestionStatus status) {
String sql = "UPDATE suggestions SET status = ? WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, status.name());
stmt.setInt(2, id);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void deleteSuggestion(int id) {
String sql = "DELETE FROM suggestions WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, id);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,9 @@
package de.winniepat.SMPPlugin.suggestions;
public enum SuggestionStatus {
NONE,
OPEN,
ACCEPTED,
REJECTED,
REVIEW
}
@@ -0,0 +1,106 @@
package de.winniepat.SMPPlugin.suggestions.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import de.winniepat.SMPPlugin.suggestions.Suggestion;
import de.winniepat.SMPPlugin.suggestions.SuggestionManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SuggestionCommand implements CommandExecutor {
private final SuggestionManager manager;
private final SMPPlugin smpPlugin;
private final Map<UUID, Long> cooldowns = new HashMap<>();
private final long COOLDOWN_MILLIS = 60 * 60 * 1000;
private final File cooldownFile;
private final YamlConfiguration cooldownData;
public SuggestionCommand(SuggestionManager manager, Plugin plugin, SMPPlugin smpPlugin) {
this.manager = manager;
this.smpPlugin = smpPlugin;
this.cooldownFile = new File(plugin.getDataFolder(), "suggestion_cooldowns.yml");
if (!cooldownFile.exists()) {
try {
cooldownFile.getParentFile().mkdirs();
cooldownFile.createNewFile();
System.out.println("[Suggestion] Created cooldowns.yml at: " + cooldownFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
this.cooldownData = YamlConfiguration.loadConfiguration(cooldownFile);
for (String key : cooldownData.getKeys(false)) {
try {
UUID uuid = UUID.fromString(key);
long timestamp = cooldownData.getLong(key);
cooldowns.put(uuid, timestamp);
} catch (IllegalArgumentException ignored) {}
}
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(smpPlugin.getMessage("command.error.no_player", Map.of("player", sender.getName())));
return true;
}
if (args.length == 0) {
player.sendMessage(smpPlugin.getMessage("command.suggestion.usage", Map.of("player", player.getName())));
return true;
}
UUID uuid = player.getUniqueId();
long now = System.currentTimeMillis();
if (cooldowns.containsKey(uuid)) {
long lastUsed = cooldowns.get(uuid);
long timeLeft = COOLDOWN_MILLIS - (now - lastUsed);
if (timeLeft > 0) {
player.sendMessage("§cPlease wait §e" + (timeLeft / 1000) + " seconds§c, before you send another Suggestion.");
return true;
}
}
String input = String.join(" ", args);
Matcher matcher = Pattern.compile("^\"([^\"]{1,50})\"\\s+(.{1,300})$").matcher(input);
if (!matcher.matches()) {
player.sendMessage(smpPlugin.getMessage("command.suggestion.usage", Map.of("player", player.getName())));
return true;
}
String title = matcher.group(1).trim();
String content = matcher.group(2).trim();
Suggestion suggestion = manager.addSuggestion(uuid, player.getName(), title, content);
if (suggestion != null) {
player.sendMessage("§aYour Suggestion was submitted with the ID §e#" + suggestion.getId());
cooldowns.put(uuid, now);
cooldownData.set(uuid.toString(), now);
try {
cooldownData.save(cooldownFile);
} catch (IOException e) {
e.printStackTrace();
}
} else {
player.sendMessage(smpPlugin.getMessage("command.suggestion.fail", Map.of("player", player.getName())));
}
return true;
}
}
@@ -0,0 +1,26 @@
package de.winniepat.SMPPlugin.suggestions.commands;
import de.winniepat.SMPPlugin.suggestions.Suggestion;
import de.winniepat.SMPPlugin.suggestions.SuggestionManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class SuggestionListCommand implements CommandExecutor {
private final SuggestionManager manager;
public SuggestionListCommand(SuggestionManager manager) {
this.manager = manager;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String @NotNull [] args) {
for (Suggestion suggestion : manager.getAllSuggestions()) {
sender.sendMessage("§7[#" + suggestion.getId() + "] §e" + suggestion.getTitle() +
" §8von §6" + suggestion.getPlayerName() + " §8(§f" + suggestion.getStatus() + "§8)");
}
return true;
}
}
@@ -0,0 +1,64 @@
package de.winniepat.SMPPlugin.suggestions.commands;
import de.winniepat.SMPPlugin.SMPPlugin;
import de.winniepat.SMPPlugin.suggestions.Suggestion;
import de.winniepat.SMPPlugin.suggestions.SuggestionManager;
import de.winniepat.SMPPlugin.suggestions.SuggestionStatus;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class SuggestionStatusCommand implements CommandExecutor {
private final SuggestionManager manager;
private final SMPPlugin smpPlugin;
public SuggestionStatusCommand(SuggestionManager manager, SMPPlugin smpPlugin) {
this.smpPlugin = smpPlugin;
this.manager = manager;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length < 2) {
sender.sendMessage(smpPlugin.getMessage("command.suggestionstatus.usage", Map.of("player", sender.getName())));
return true;
}
SuggestionStatus status;
switch (args[0].toLowerCase()) {
case "accept" -> status = SuggestionStatus.ACCEPTED;
case "reject" -> status = SuggestionStatus.REJECTED;
case "review" -> status = SuggestionStatus.REVIEW;
default -> {
sender.sendMessage("§cInvalid Status: " + args[0]);
return true;
}
}
try {
int id = Integer.parseInt(args[1]);
Suggestion suggestion = manager.getSuggestionById(id);
if (suggestion == null) {
sender.sendMessage("§cNo suggestion found with ID " + id);
return true;
}
manager.updateStatus(id, status);
sender.sendMessage("§aSuggestion #" + id + " was set to " + status);
Player player = Bukkit.getPlayer(suggestion.getPlayerUuid());
if (player != null && player.isOnline()) {
player.sendMessage("§aYour suggestion (#" + id + ") was set to §e" + status);
}
} catch (NumberFormatException e) {
sender.sendMessage("§cInvalid ID: " + args[1]);
}
return true;
}
}
@@ -0,0 +1,44 @@
package de.winniepat.SMPPlugin.waypoints;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import java.util.Map;
public class GiveWaypointStoneListener implements Listener {
private final SMPPlugin smpPlugin;
public GiveWaypointStoneListener(SMPPlugin smpPlugin) {
this.smpPlugin = smpPlugin;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!player.hasPlayedBefore()) {
ItemStack item = createWaypointStone();
player.getInventory().addItem(item);
player.sendMessage(smpPlugin.getMessage("waypoint.first_join", Map.of("player", player.getName())));
}
}
private ItemStack createWaypointStone() {
ItemStack item = new ItemStack(Material.LODESTONE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.AQUA + "Waypoint Stone");
meta.getPersistentDataContainer().set(new NamespacedKey(SMPPlugin.getInstance(), "waypoint"), PersistentDataType.STRING, "true");
item.setItemMeta(meta);
return item;
}
}
@@ -0,0 +1,16 @@
package de.winniepat.SMPPlugin.waypoints;
import org.bukkit.Location;
public class Waypoint {
private final String name;
private final Location location;
public Waypoint(String name, Location location) {
this.name = name;
this.location = location;
}
public String getName() { return name; }
public Location getLocation() { return location; }
}
@@ -0,0 +1,56 @@
package de.winniepat.SMPPlugin.waypoints;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import java.util.Map;
public class WaypointCommands implements CommandExecutor {
private final WaypointsDatabase sqliteManager;
private final SMPPlugin smpPlugin;
public WaypointCommands(WaypointsDatabase sqliteManager, SMPPlugin smpPlugin) {
this.sqliteManager = sqliteManager;
this.smpPlugin = smpPlugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player player)) return false;
if (label.equalsIgnoreCase("getwaypoint")) {
player.getInventory().addItem(createWaypointStone());
player.sendMessage(smpPlugin.getMessage("getwaypoint.success", Map.of("player", player.getName())));
return true;
}
if (label.equalsIgnoreCase("addwaypoint") && args.length >= 1) {
String name = String.join(" ", args);
boolean success = sqliteManager.saveWaypoint(player.getUniqueId(), new Waypoint(name, player.getLocation()));
if (success) {
player.sendMessage(ChatColor.GREEN + "Waypoint '" + name + "' saved.");
} else {
player.sendMessage(smpPlugin.getMessage("addwaypoint.already_exists", Map.of("player", player.getName())));
}
return true;
}
return false;
}
private ItemStack createWaypointStone() {
ItemStack item = new ItemStack(Material.LODESTONE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.AQUA + "Your Waypoints");
meta.getPersistentDataContainer().set(new NamespacedKey(SMPPlugin.getInstance(), "waypoint"), PersistentDataType.STRING, "true");
item.setItemMeta(meta);
return item;
}
}
@@ -0,0 +1,152 @@
package de.winniepat.SMPPlugin.waypoints;
import de.winniepat.SMPPlugin.SMPPlugin;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.*;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
public class WaypointListener implements Listener {
private final WaypointsDatabase sqliteManager;
private final Map<UUID, Location> pendingWaypoints = new HashMap<>();
private final Map<UUID, Location> pendingLocations = new HashMap<>();
private final SMPPlugin smpPlugin;
public WaypointListener(WaypointsDatabase sqliteManager, SMPPlugin smpPlugin) {
this.sqliteManager = sqliteManager;
this.smpPlugin = smpPlugin;
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent e) {
Player player = e.getPlayer();
if (e.getItem() == null || !e.getItem().hasItemMeta()) return;
ItemMeta meta = e.getItem().getItemMeta();
if (!"true".equals(meta.getPersistentDataContainer()
.get(new NamespacedKey(SMPPlugin.getInstance(), "waypoint"), PersistentDataType.STRING))) return;
if (e.getAction() == Action.RIGHT_CLICK_BLOCK) {
e.setCancelled(true);
Block block = e.getClickedBlock();
if (block == null) return;
Location loc = block.getLocation().add(0.5, 1, 0.5);
pendingWaypoints.put(player.getUniqueId(), loc);
pendingLocations.put(player.getUniqueId(), player.getLocation().clone());
player.sendMessage(ChatColor.YELLOW + "Please enter a name for your waypoint in chat. Don't move!");
} else if (e.getAction() == Action.RIGHT_CLICK_AIR) {
e.setCancelled(true);
openWaypointMenu(player);
}
}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent e) {
Player player = e.getPlayer();
if (pendingWaypoints.containsKey(player.getUniqueId())) {
e.setCancelled(true);
String name = e.getMessage().trim();
Location loc = pendingWaypoints.remove(player.getUniqueId());
pendingLocations.remove(player.getUniqueId());
Bukkit.getScheduler().runTask(SMPPlugin.getInstance(), () -> {
boolean success = sqliteManager.saveWaypoint(player.getUniqueId(), new Waypoint(name, loc));
if (success) {
player.sendMessage(ChatColor.GREEN + "Waypoint '" + name + "' set!");
player.playSound(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 1f);
} else {
player.sendMessage(smpPlugin.getMessage("waypointlistener.already_exists", Map.of("player", player.getName())));
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f);
}
});
}
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent e) {
Player player = e.getPlayer();
if (pendingLocations.containsKey(player.getUniqueId())) {
Location from = pendingLocations.get(player.getUniqueId());
if (e.getTo() != null && e.getTo().distance(from) > 0.1) {
pendingWaypoints.remove(player.getUniqueId());
pendingLocations.remove(player.getUniqueId());
player.sendMessage(smpPlugin.getMessage("waypointlistener.movement", Map.of("player", player.getName())));
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 0.5f);
}
}
}
public void openWaypointMenu(Player player) {
List<Waypoint> waypoints = sqliteManager.getWaypoints(player.getUniqueId());
Inventory gui = Bukkit.createInventory(null, 9 * 3, "Your Waypoints");
for (Waypoint wp : waypoints) {
ItemStack item = new ItemStack(Material.LODESTONE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.YELLOW + wp.getName());
item.setItemMeta(meta);
gui.addItem(item);
}
player.openInventory(gui);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent e) {
if (e.getView().getTitle().equals("Your Waypoints")) {
e.setCancelled(true);
ItemStack clicked = e.getCurrentItem();
if (clicked != null && clicked.hasItemMeta()) {
String name = ChatColor.stripColor(clicked.getItemMeta().getDisplayName());
Player player = (Player) e.getWhoClicked();
List<Waypoint> waypoints = sqliteManager.getWaypoints(player.getUniqueId());
for (Waypoint wp : waypoints) {
if (wp.getName().equals(name)) {
player.closeInventory();
startTeleportCountdown(player, wp);
break;
}
}
}
}
}
private void startTeleportCountdown(Player player, Waypoint waypoint) {
Location initialLoc = player.getLocation().clone();
player.sendMessage(ChatColor.YELLOW + "Teleporting to " + waypoint.getName() + " in 5 seconds. Don't move!");
new BukkitRunnable() {
int countdown = 5;
@Override
public void run() {
if (!player.isOnline() || player.getLocation().distance(initialLoc) > 0.1) {
player.sendMessage(ChatColor.RED + "Teleport cancelled due to movement.");
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 0.5f);
cancel();
return;
}
if (countdown > 0) {
player.sendActionBar(ChatColor.YELLOW + "Teleporting in " + countdown + "...");
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1f, 1f);
countdown--;
} else {
player.teleport(waypoint.getLocation());
player.sendMessage(ChatColor.GREEN + "Teleported to " + waypoint.getName() + "!");
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f);
cancel();
}
}
}.runTaskTimer(SMPPlugin.getInstance(), 0L, 20L);
}
}
@@ -0,0 +1,82 @@
package de.winniepat.SMPPlugin.waypoints;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import java.sql.*;
import java.util.*;
public class WaypointsDatabase {
private final String url = "jdbc:sqlite:plugins/SMPPlugin/waypoints.db";
public WaypointsDatabase() {
try (Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement()) {
stmt.executeUpdate("""
CREATE TABLE IF NOT EXISTS waypoints (
player_uuid TEXT,
name TEXT,
world TEXT,
x DOUBLE,
y DOUBLE,
z DOUBLE
)
""");
} catch (SQLException e) {
e.printStackTrace();
}
}
public boolean saveWaypoint(UUID playerUuid, Waypoint waypoint) {
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement check = conn.prepareStatement("""
SELECT 1 FROM waypoints WHERE player_uuid = ? AND name = ?
""")) {
check.setString(1, playerUuid.toString());
check.setString(2, waypoint.getName());
ResultSet rs = check.executeQuery();
if (rs.next()) {
return false;
}
} catch (SQLException e) {
e.printStackTrace();
}
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement ps = conn.prepareStatement("""
INSERT INTO waypoints (player_uuid, name, world, x, y, z) VALUES (?, ?, ?, ?, ?, ?)
""")) {
ps.setString(1, playerUuid.toString());
ps.setString(2, waypoint.getName());
ps.setString(3, waypoint.getLocation().getWorld().getName());
ps.setDouble(4, waypoint.getLocation().getX());
ps.setDouble(5, waypoint.getLocation().getY());
ps.setDouble(6, waypoint.getLocation().getZ());
ps.executeUpdate();
return true;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public List<Waypoint> getWaypoints(UUID playerUuid) {
List<Waypoint> list = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement ps = conn.prepareStatement("""
SELECT name, world, x, y, z FROM waypoints WHERE player_uuid = ?
""")) {
ps.setString(1, playerUuid.toString());
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("name");
String world = rs.getString("world");
double x = rs.getDouble("x"), y = rs.getDouble("y"), z = rs.getDouble("z");
list.add(new Waypoint(name, new Location(Bukkit.getWorld(world), x, y, z)));
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
+56
View File
@@ -0,0 +1,56 @@
command.error.no_player: "§cOnly players are able to execute this command."
command.error.no_permission: "§cYou don't have permissions to do that."
# Coding Command
command.coding.usage: "§cUse /coding on or /coding off"
command.coding.already: "§eYou're already marked as coding."
command.coding.marked: "§aYou are now marked as coding."
command.coding.unmarked: "§aYou are no longer marked as coding."
command.coding.error: "§cError while marking you as coding."
# Poll Command
command.poll.usage: "§c/pollstart \"question\" Duration Option1 Option2 ..."
command.poll.already_active: "§cThere is already an active poll."
command.poll.invalid_time: "§cInvalid Time. Choose a number for seconds."
# Vote GUI
pollvotegui.already_voted: "§cYou already voted."
# Report
command.report.invalid_player: "§cInvalid Player."
command.report.reason: "§cPlease tell us exactly the reason."
command.report.success: "§aReport Saved! Staff will look at it."
command.report.usage: "§7Use: §e/report <Player> <Reason>"
# ReportActionGUI
reportactiongui.not_found: "§cReport not found."
reportactiongui.successful_delete: "§aReport deleted."
reportactiongui.successful_ban: "§cPlayer was permanently banned."
# Suggestion Command
command.suggestion.usage: "§7Use: §e/suggestion \"Title with quotes\" My super Suggestion"
command.suggestion.fail: "§cAn Error occurred. Your Suggestion couldn't be submitted."
# SuggestionStatus Command
command.suggestionstatus.usage: "§7Benutze: §e/suggestionstatus <accept|reject|review> <ID>"
# Suggestion GUI
suggestiongui.processing_error: "§cError while processing the suggestion-id."
# Waypoint System
waypoint.first_join: "§6You've received your first Waystone! Use it to set and teleport to waypoints."
# GiveWaypointStoneListener
getwaypoint.success: "&aWaypoint Stone added to your inventory."
# addwaypoint Command
addwaypoint.already_exists: "§cA waypoint with that name already exists."
# WaypointListener
waypointlistener.already_exists: "§cA waypoint with that name already exists."
waypointlistener.movement: "§cWaypoint creation cancelled due to movement."
# PlayerSneakListener
blockelevators.on_cooldown: "§cElevator is cooling down"
blockelevators.no_elevator_below: "§eNo elevator block below!"
blockelevators.no_elevator_above: "§eNo elevator block above!"
+70
View File
@@ -0,0 +1,70 @@
name: SMPPlugin
version: '1.0'
main: de.winniepat.SMPPlugin.SMPPlugin
api-version: '1.21'
load: STARTUP
authors: [ WinniePat ]
website: https://winniepat.de
commands:
trash:
description: Öffne den Mülleimer zum Löschen von Items
report:
description: Spieler melden
reports:
description: Alle Reports anzeigen
permission: smputils.reports
suggestion:
description: Sende einen Vorschlag
usage: /<command> <Titel> | <Inhalt>
suggestions:
description: Zeigt alle Vorschläge im Chat
permission: smpplugin.suggestions.use
aliases:
- vorschlag
suggestionstatus:
description: Setze den Status eines Vorschlags
usage: /<command> <accept|reject|review> <ID>
permission: smpplugin.suggestion.status
suggestionsgui:
description: Öffne die Vorschlags-GUI
permission: smpplugin.suggestion.gui
coding:
description: Toggle coding status
usage: /coding <on|off>
permission: smpplugin.command.coding.use
winnie:
description: Toggle winnie status
usage: /winnie <on|off>
permission: smpplugin.command.winnie.use
pollstart:
description: Start a poll
aliases:
- poll
pollvote:
description: Vote on a poll
aliases:
- vote
pollresults:
description: Shows the results
bloodmoon:
description: Toggle the Bloodmoon event
usage: /bloodmoon
permission: smpplugin.bloodmoon.toggle
blackmarket:
description: Manually spawns the Black Market trader
usage: /blackmarket
permission: smpplugin.blackmarket.spawn
getwaypoint:
description: Get the waypoint stone
permission: smpplugin.waypoints.getwaypoint
addwaypoint:
description: Add a new waypoint
smp:
description: start
usage: /smp start
spawn:
description: spawn
usage: /spawn
shop:
description: shop
usage: /shop