added combat logging kill, and a refined new welcome message
This commit is contained in:
parent
a4eaa53b23
commit
96fccb1041
62 changed files with 287 additions and 2 deletions
BIN
.gradle/9.6.1/checksums/checksums.lock
Normal file
BIN
.gradle/9.6.1/checksums/checksums.lock
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/checksums/md5-checksums.bin
Normal file
BIN
.gradle/9.6.1/checksums/md5-checksums.bin
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/checksums/sha1-checksums.bin
Normal file
BIN
.gradle/9.6.1/checksums/sha1-checksums.bin
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/executionHistory/executionHistory.bin
Normal file
BIN
.gradle/9.6.1/executionHistory/executionHistory.bin
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/executionHistory/executionHistory.lock
Normal file
BIN
.gradle/9.6.1/executionHistory/executionHistory.lock
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/fileChanges/last-build.bin
Normal file
BIN
.gradle/9.6.1/fileChanges/last-build.bin
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/fileHashes/fileHashes.bin
Normal file
BIN
.gradle/9.6.1/fileHashes/fileHashes.bin
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/fileHashes/fileHashes.lock
Normal file
BIN
.gradle/9.6.1/fileHashes/fileHashes.lock
Normal file
Binary file not shown.
BIN
.gradle/9.6.1/fileHashes/resourceHashesCache.bin
Normal file
BIN
.gradle/9.6.1/fileHashes/resourceHashesCache.bin
Normal file
Binary file not shown.
0
.gradle/9.6.1/gc.properties
Normal file
0
.gradle/9.6.1/gc.properties
Normal file
Binary file not shown.
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
Binary file not shown.
2
.gradle/buildOutputCleanup/cache.properties
Normal file
2
.gradle/buildOutputCleanup/cache.properties
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#Fri Sep 04 09:55:07 CEST 2026
|
||||
gradle.version=9.6.1
|
||||
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
鋸D<E98BB8>OX"<22>髀▲s蘓<73>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
ÑuWiPº¢Ïnì7a¶—àut
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
X&¿I<C2BF>ה<EFBFBD>±@במ¦gK<67>ֱ<>ֵQ©L%
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
®=Γps_οΔ‡μ²;m«KRας
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
04874f85-d75a-4b9f-91ec-9f227f2a092¹
|
||||
|
|
@ -0,0 +1 @@
|
|||
341fbea8-17c8-4ff9-abb1-8cf0a8d07b3ś
|
||||
Binary file not shown.
Binary file not shown.
BIN
.gradle/configuration-cache/configuration-cache.lock
Normal file
BIN
.gradle/configuration-cache/configuration-cache.lock
Normal file
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
4ad0b03c-7d47-4056-990b-3a93c3db11fá
|
||||
Binary file not shown.
0
.gradle/configuration-cache/gc.properties
Normal file
0
.gradle/configuration-cache/gc.properties
Normal file
BIN
.gradle/file-system.probe
Normal file
BIN
.gradle/file-system.probe
Normal file
Binary file not shown.
0
.gradle/vcs-1/gc.properties
Normal file
0
.gradle/vcs-1/gc.properties
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
5
.idea/misc.xml
generated
Normal file
5
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -6,6 +6,7 @@ A quality-of-life SMP plugin for Paper/Folia.
|
|||
|
||||
- **Join messages** — friendly welcome messages, including a first-join message that lists the server's plugins, and a special announcement when the owner logs in.
|
||||
- **`/nopvp`** — toggle no-PvP mode. While active you cannot damage other players and other players cannot damage you (direct hits and projectiles). While enabled you also glow so everyone can see you're protected. Turning no-PvP *off* starts a **1-hour cooldown** before you can enable it again, and the state persists across restarts so relogging can't bypass it.
|
||||
- **Anti-combat-logging** — whenever you hit another player or get hit by one, you're tagged as being in combat for **15 seconds** with a chat warning. Logging off while tagged kills you on the spot and announces it to the server.
|
||||
|
||||
## Commands
|
||||
|
||||
|
|
@ -27,9 +28,11 @@ Player no-PvP state and cooldown timestamps are stored in `plugins/qol-tweaks/no
|
|||
## Building
|
||||
|
||||
```bash
|
||||
./gradlew build
|
||||
./build.sh
|
||||
```
|
||||
|
||||
This just runs `./gradlew build`, but if the pinned Java toolchain isn't installed it will automatically fall back to whatever JDK you do have (keeping the jar's bytecode compatible). You can also pass extra Gradle args, e.g. `./build.sh clean build`.
|
||||
|
||||
The plugin jar is output to `build/libs/`.
|
||||
|
||||
## License
|
||||
|
|
|
|||
101
build.sh
Executable file
101
build.sh
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds the qol-tweaks plugin jar.
|
||||
#
|
||||
# The build pins a Java toolchain version in build.gradle.kts. This script
|
||||
# finds that version on your machine, and if it isn't installed it transparently
|
||||
# falls back to the highest JDK that is (so `./build.sh` just works).
|
||||
#
|
||||
# Usage: ./build.sh [extra gradle args...]
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# --- 1. which Java version does the build pin? --------------------------------
|
||||
pinned="$(grep -oE 'JavaLanguageVersion\.of\([0-9]+\)' build.gradle.kts | grep -oE '[0-9]+' | head -1 || true)"
|
||||
pinned="${pinned:-25}"
|
||||
|
||||
# --- 2. find an installed JDK for a given version ------------------------------
|
||||
java_major() { # $1 = path to java binary; echoes its major version (e.g. 26)
|
||||
"$1" -version 2>&1 | grep -oE '"([0-9]+)' | head -1 | tr -d '"'
|
||||
}
|
||||
|
||||
find_java_home() { # $1 = version (e.g. 25); echoes a home whose java matches, or fails
|
||||
local -a candidates=()
|
||||
if [[ -x /usr/libexec/java_home ]]; then
|
||||
local mac_home
|
||||
if mac_home="$(/usr/libexec/java_home -v "$1" 2>/dev/null)"; then
|
||||
candidates+=("$mac_home")
|
||||
fi
|
||||
fi
|
||||
[[ -n "${JAVA_HOME:-}" ]] && candidates+=("$JAVA_HOME")
|
||||
|
||||
local home bin ver
|
||||
for home in "${candidates[@]}"; do
|
||||
bin="$home/bin/java"
|
||||
[[ -x "$bin" ]] || continue
|
||||
ver="$(java_major "$bin")"
|
||||
if [[ -n "$ver" && "$ver" == "$1" ]]; then
|
||||
echo "$home"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- 3. pick the JDK to build with --------------------------------------------
|
||||
selected=""
|
||||
selected_home=""
|
||||
for v in "$pinned" 26 25 24 23 22 21 20 17; do
|
||||
if h="$(find_java_home "$v")"; then
|
||||
selected="$v"
|
||||
selected_home="$h"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$selected" ]]; then
|
||||
echo "error: no JDK found. Install any JDK 17+ (e.g. Java 25) and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export JAVA_HOME="$selected_home"
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
|
||||
gradle_args=()
|
||||
override_init=""
|
||||
|
||||
if [[ "$selected" != "$pinned" ]]; then
|
||||
echo "note: pinned JDK $pinned not found; building with installed JDK $selected."
|
||||
override_init="${TMPDIR:-/tmp}/qol-tweaks-toolchain-$pinned-$selected.gradle"
|
||||
cat > "$override_init" <<EOF
|
||||
gradle.afterProject { p ->
|
||||
if (p.plugins.hasPlugin('java')) {
|
||||
p.extensions.findByName('java').toolchain.languageVersion.set(JavaLanguageVersion.of($selected))
|
||||
p.tasks.withType(JavaCompile).configureEach {
|
||||
options.release.set($pinned)
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
gradle_args+=(-I "$override_init")
|
||||
# the init script is external input, so a cached configuration would be stale
|
||||
gradle_args+=(--no-configuration-cache)
|
||||
fi
|
||||
|
||||
trap '[[ -n "$override_init" ]] && rm -f "$override_init"' EXIT
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
gradle_args+=(build)
|
||||
else
|
||||
gradle_args+=("$@")
|
||||
fi
|
||||
|
||||
echo "Using JDK $selected at $JAVA_HOME"
|
||||
./gradlew "${gradle_args[@]}"
|
||||
|
||||
jar="$(ls -1 build/libs/*.jar 2>/dev/null | grep -Ev '(-sources|-javadoc)\.jar$' | head -1 || true)"
|
||||
if [[ -n "$jar" ]]; then
|
||||
echo
|
||||
echo "Done. Plugin jar: $(pwd)/$jar"
|
||||
fi
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.dvorakmortonxd.qoltweaks;
|
||||
|
||||
import com.dvorakmortonxd.qoltweaks.combatlog.CombatLogListener;
|
||||
import com.dvorakmortonxd.qoltweaks.combatlog.CombatLogManager;
|
||||
import com.dvorakmortonxd.qoltweaks.listeners.JoinListener;
|
||||
import com.dvorakmortonxd.qoltweaks.nopvp.NoPvPCommand;
|
||||
import com.dvorakmortonxd.qoltweaks.nopvp.NoPvPListener;
|
||||
|
|
@ -10,14 +12,17 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
public final class QoLtweaks extends JavaPlugin {
|
||||
|
||||
private NoPvPManager noPvPManager;
|
||||
private CombatLogManager combatLogManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
noPvPManager = new NoPvPManager(this);
|
||||
combatLogManager = new CombatLogManager(this);
|
||||
|
||||
getServer().getPluginManager().registerEvents(new JoinListener(), this);
|
||||
getServer().getPluginManager().registerEvents(new NoPvPListener(noPvPManager), this);
|
||||
getServer().getPluginManager().registerEvents(new CombatLogListener(this, combatLogManager), this);
|
||||
|
||||
PluginCommand noPvPCommand = getCommand("nopvp");
|
||||
if (noPvPCommand != null) {
|
||||
|
|
@ -31,5 +36,8 @@ public final class QoLtweaks extends JavaPlugin {
|
|||
if (noPvPManager != null) {
|
||||
noPvPManager.save();
|
||||
}
|
||||
if (combatLogManager != null) {
|
||||
combatLogManager.cancelNotices();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package com.dvorakmortonxd.qoltweaks.combatlog;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class CombatLogListener implements Listener {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final CombatLogManager manager;
|
||||
|
||||
public CombatLogListener(JavaPlugin plugin, CombatLogManager manager) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getEntity() instanceof Player victim)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player attacker = resolveAttacker(event.getDamager());
|
||||
if (attacker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker.getUniqueId().equals(victim.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
manager.tag(victim, true);
|
||||
manager.tag(attacker, false);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (!plugin.isEnabled() || player.isDead() || !manager.isTagged(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.setHealth(0.0);
|
||||
plugin.getServer().getOnlinePlayers().forEach(other ->
|
||||
other.sendMessage("§c" + player.getName() + " combat-logged and died!"));
|
||||
}
|
||||
|
||||
private Player resolveAttacker(Entity damager) {
|
||||
if (damager instanceof Player player) {
|
||||
return player;
|
||||
}
|
||||
if (damager instanceof Projectile projectile && projectile.getShooter() instanceof Player player) {
|
||||
return player;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.dvorakmortonxd.qoltweaks.combatlog;
|
||||
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class CombatLogManager {
|
||||
|
||||
public static final long TAG_MILLIS = 15 * 1000L; // 15 seconds
|
||||
|
||||
private static final String VICTIM_MESSAGE = "§cYou got hit! Don't log off for 15 seconds or you'll insta-die.";
|
||||
private static final String ATTACKER_MESSAGE = "§cYou hit a player! Don't log off for 15 seconds or you'll insta-die.";
|
||||
private static final String SAFE_MESSAGE = "§aYou are no longer in combat.";
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final Map<UUID, Long> taggedUntil = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, ScheduledTask> notices = new ConcurrentHashMap<>();
|
||||
|
||||
public CombatLogManager(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void tag(Player player, boolean victim) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
boolean firstTag = !isTagged(player);
|
||||
taggedUntil.put(uuid, System.currentTimeMillis() + TAG_MILLIS);
|
||||
|
||||
if (firstTag) {
|
||||
player.sendMessage(victim ? VICTIM_MESSAGE : ATTACKER_MESSAGE);
|
||||
}
|
||||
scheduleNotice(player);
|
||||
}
|
||||
|
||||
public boolean isTagged(Player player) {
|
||||
return getRemainingMillis(player) > 0;
|
||||
}
|
||||
|
||||
public long getRemainingMillis(Player player) {
|
||||
long until = taggedUntil.getOrDefault(player.getUniqueId(), 0L);
|
||||
return Math.max(0L, until - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void scheduleNotice(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
ScheduledTask previous = notices.remove(uuid);
|
||||
if (previous != null) {
|
||||
previous.cancel();
|
||||
}
|
||||
long delayTicks = (TAG_MILLIS / 1000L) * 20L;
|
||||
ScheduledTask task = plugin.getServer().getGlobalRegionScheduler()
|
||||
.runDelayed(plugin, scheduled -> {
|
||||
notices.remove(uuid);
|
||||
if (player.isOnline() && !isTagged(player)) {
|
||||
player.sendMessage(SAFE_MESSAGE);
|
||||
}
|
||||
}, delayTicks);
|
||||
notices.put(uuid, task);
|
||||
}
|
||||
|
||||
public void cancelNotices() {
|
||||
for (ScheduledTask task : notices.values()) {
|
||||
task.cancel();
|
||||
}
|
||||
notices.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,20 @@ public class JoinListener implements Listener {
|
|||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
player.sendTitle("§aWelcome to the SMP", "§7on a new world since 4th September 2026", 10, 70, 20);
|
||||
|
||||
if (!player.hasPlayedBefore()) {
|
||||
player.sendMessage("§aWelcome to the SMP, " + player.getName() + "!\nThis is your first time here, so let me tell you whats on here, theres a 3d map of the server at https://bluemap.dvorakmortonxd.dev , theres a voicechat you can optionally install as a mod (Simple Voice Chat), theres a tree chopper plugin you can toggle with /atc, theres veinminer which you can also toggle with /veinminer toggle, theres tradecycle plugin which allows you to cycle thru villagers trades by clicking [F] in any inventory slot while trading and there is ClickVillagers which allows you to pick up villagers into your inventory when shifting and right clicking,\n thats about it, enjoy your game! :3");
|
||||
player.sendMessage("§aWelcome to the SMP, " + player.getName() + "!");
|
||||
player.sendMessage("This is your first time here, so let me tell you whats on here:");
|
||||
player.sendMessage("§7- a 3d map of the server at https://bluemap.dvorakmortonxd.dev");
|
||||
player.sendMessage("§7- a voicechat you can optionally install as a mod (Simple Voice Chat)");
|
||||
player.sendMessage("§7- a tree chopper plugin you can toggle with /atc");
|
||||
player.sendMessage("§7- veinminer, which you can also toggle with /veinminer toggle");
|
||||
player.sendMessage("§7- tradecycle, which lets you cycle thru villagers trades by pressing [F] in any inventory slot while trading");
|
||||
player.sendMessage("§7- ClickVillagers, which lets you pick up villagers into your inventory by shift-right-clicking");
|
||||
player.sendMessage("§7- a /nopvp command to toggle no-pvp mode if you want to build in peace");
|
||||
player.sendMessage("§cAbuse of /nopvp is bannable. Also, combat logging is NOT a good idea.");
|
||||
player.sendMessage("§aThats about it, enjoy your game! :3");
|
||||
event.setJoinMessage("§e" + player.getName() + " has joined the SMP for the first time!");
|
||||
} else {
|
||||
if (player.getName().equals("dvorakmortonxd")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue