Upload via GUI (14 Dateien)

This commit is contained in:
2026-11-07 22:06:13 +00:00
parent ea618e30d6
commit b7d1f92793
5 changed files with 292 additions and 63 deletions
+17 -1
View File
@@ -27,7 +27,7 @@ Folia wird **nicht** unterstützt.
## 2. Installation (5 Schritte)
1. Stoppe den Server (oder lass ihn laufen, ein Neustart ist trotzdem später nötig).
2. Kopiere die Datei `LandsChunkInfo-1.0.0.jar` in den Ordner `plugins` deines Servers. Dort liegt auch die Lands-Jar.
2. Kopiere die Datei `LandsChunkInfo-1.0.2.jar` in den Ordner `plugins` deines Servers. Dort liegt auch die Lands-Jar.
3. Starte den Server neu.
4. Schau in die Konsole. Du solltest `[LandsChunkInfo] Enabling LandsChunkInfo` sehen. Steht dort ein roter Fehler, lies Abschnitt 9.
5. Im Ordner `plugins/LandsChunkInfo/` wurde jetzt eine Datei `config.yml` erstellt. Dort kannst du alles einstellen (Abschnitt 5).
@@ -296,3 +296,19 @@ Wenn du nicht weiterkommst: Kopiere die **roten Zeilen aus der Server-Konsole**
- Die Warteschlange und die Watchlist arbeiten nur für Spieler, die **online** sind.
- Temp-Chunks laufen erst ab, wenn der Ersteller oder der Besitzer des Lands online ist.
- Lands-Limits (z. B. maximale Chunks) kann das Plugin nicht verändern.
---
## 11. PvP-Regionen (neu in 1.0.2)
Lands blockiert PvP in der Wildnis zwischen Spielern, die im selben Land Mitglied sind. Mit PvP-Regionen kannst du Bereiche festlegen, in denen trotzdem gekämpft werden darf (z. B. eine Arena).
| Befehl | Was passiert |
|---|---|
| `/chunkinfo pvp pos1` | Setzt die erste Ecke an deiner Position |
| `/chunkinfo pvp pos2` | Setzt die zweite Ecke an deiner Position |
| `/chunkinfo pvp create <Name>` | Legt die Region an |
| `/chunkinfo pvp delete <Name>` | Löscht die Region |
| `/chunkinfo pvp list` | Zeigt alle Regionen |
Dafür brauchst du das Recht `landschunkinfo.admin`. Die Regionen sind Rechtecke über die ganze Höhe und werden in `plugins/LandsChunkInfo/pvp-regions.yml` gespeichert. PvP wird nur freigegeben, wenn Angreifer und Opfer beide in der Region stehen.
+61 -61
View File
@@ -1,61 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.landschunkinfo</groupId>
<artifactId>LandsChunkInfo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.angeschossen</groupId>
<artifactId>lands</artifactId>
<version>8.8.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/Lands-8.8.0.jar</systemPath>
</dependency>
<dependency>
<groupId>net.milkbowl</groupId>
<artifactId>vaultapi</artifactId>
<version>1.7.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/VaultAPI-1.7.1.jar</systemPath>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.7</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/placeholderapi-2.11.7.jar</systemPath>
</dependency>
</dependencies>
<build>
<finalName>LandsChunkInfo-${project.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.landschunkinfo</groupId>
<artifactId>LandsChunkInfo</artifactId>
<version>1.0.2</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.angeschossen</groupId>
<artifactId>lands</artifactId>
<version>8.8.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/Lands-8.8.0.jar</systemPath>
</dependency>
<dependency>
<groupId>net.milkbowl</groupId>
<artifactId>vaultapi</artifactId>
<version>1.7.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/VaultAPI-1.7.1.jar</systemPath>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.7</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/placeholderapi-2.11.7.jar</systemPath>
</dependency>
</dependencies>
<build>
<finalName>LandsChunkInfo-${project.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
@@ -21,7 +21,7 @@ import java.util.UUID;
final class InfoCommand implements CommandExecutor, TabCompleter {
private static final List<String> SUBS = List.of("top", "skip", "gift", "queue", "watch", "temp", "reload");
private static final List<String> SUBS = List.of("top", "skip", "gift", "queue", "watch", "temp", "pvp", "reload");
private final LandsChunkInfo plugin;
@@ -58,11 +58,52 @@ final class InfoCommand implements CommandExecutor, TabCompleter {
case "queue" -> queue(p, args);
case "watch" -> watch(p, args);
case "temp" -> temp(p, args);
case "pvp" -> pvp(p, args);
default -> info(p, args);
}
return true;
}
private void pvp(Player p, String[] args) {
if (!allowed(p, "admin")) {
return;
}
String action = args.length < 2 ? "" : args[1].toLowerCase(Locale.ROOT);
switch (action) {
case "pos1" -> {
plugin.pvp.setPos1(p);
plugin.send(p, "&aPunkt 1 gesetzt.");
}
case "pos2" -> {
plugin.pvp.setPos2(p);
plugin.send(p, "&aPunkt 2 gesetzt.");
}
case "create" -> {
if (args.length < 3) {
plugin.send(p, "&cNutzung: /chunkinfo pvp create <Name>");
return;
}
String err = plugin.pvp.create(p, args[2]);
plugin.send(p, err == null ? "&aPvP-Region &e" + args[2] + " &aerstellt." : "&c" + err);
}
case "delete" -> {
if (args.length < 3) {
plugin.send(p, "&cNutzung: /chunkinfo pvp delete <Name>");
return;
}
plugin.send(p, plugin.pvp.delete(args[2]) ? "&aRegion geloescht." : "&cRegion nicht gefunden.");
}
case "list" -> {
if (plugin.pvp.all().isEmpty()) {
plugin.send(p, "&7Keine PvP-Regionen vorhanden.");
}
plugin.pvp.all().values().forEach(r -> p.sendMessage(plugin.line("&8- &e" + r.name() + " &7(" + r.world()
+ ": " + r.minX() + "," + r.minZ() + " bis " + r.maxX() + "," + r.maxZ() + ")")));
}
default -> plugin.send(p, "&7/chunkinfo pvp pos1 | pos2 | create <Name> | delete <Name> | list");
}
}
private boolean allowed(Player p, String perm) {
if (p.hasPermission("landschunkinfo." + perm)) {
return true;
@@ -467,6 +508,13 @@ final class InfoCommand implements CommandExecutor, TabCompleter {
switch (args[0].toLowerCase(Locale.ROOT)) {
case "queue", "watch" -> out.addAll(args.length == 2 ? List.of("list", "clear") : List.of());
case "temp" -> out.addAll(List.of("list", "cancel", "1", "7", "14", "30"));
case "pvp" -> {
if (args.length == 2) {
out.addAll(List.of("pos1", "pos2", "create", "delete", "list"));
} else if (args[1].equalsIgnoreCase("delete")) {
out.addAll(plugin.pvp.names());
}
}
case "skip", "gift" -> {
out.add("confirm");
plugin.landsOf(p.getUniqueId()).forEach(l -> out.add(l.getName()));
@@ -49,6 +49,7 @@ public final class LandsChunkInfo extends JavaPlugin implements Listener {
LandsIntegration lands;
LockStore store;
VaultHook vault;
PvpRegions pvp;
private long cooldownMillis;
private long lastTick = System.currentTimeMillis();
@@ -72,6 +73,8 @@ public final class LandsChunkInfo extends JavaPlugin implements Listener {
new ChunkExpansion(this).register();
}
pvp = new PvpRegions(getDataFolder(), getLogger());
Bukkit.getPluginManager().registerEvents(pvp, this);
Bukkit.getPluginManager().registerEvents(this, this);
InfoCommand command = new InfoCommand(this);
var cmd = getCommand("chunkinfo");
@@ -0,0 +1,162 @@
package de.landschunkinfo;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
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 java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* PvP-Regionen: In diesen Bereichen darf gekaempft werden, auch wenn Lands den Angriff
* (z. B. wegen gemeinsamem Land in der Wildnis) blockiert. Gespeichert in pvp-regions.yml.
* Die Regionen sind Rechtecke ueber die gesamte Hoehe.
*/
final class PvpRegions implements Listener {
record Region(String name, String world, int minX, int minZ, int maxX, int maxZ) {
boolean contains(Location l) {
return l.getWorld() != null && l.getWorld().getName().equals(world)
&& l.getBlockX() >= minX && l.getBlockX() <= maxX
&& l.getBlockZ() >= minZ && l.getBlockZ() <= maxZ;
}
}
private final File file;
private final Logger log;
private final Map<String, Region> regions = new LinkedHashMap<>();
private final Map<UUID, Location> pos1 = new ConcurrentHashMap<>();
private final Map<UUID, Location> pos2 = new ConcurrentHashMap<>();
PvpRegions(File dataFolder, Logger log) {
this.file = new File(dataFolder, "pvp-regions.yml");
this.log = log;
load();
}
private void load() {
regions.clear();
if (!file.exists()) {
return;
}
YamlConfiguration y = YamlConfiguration.loadConfiguration(file);
ConfigurationSection sec = y.getConfigurationSection("regions");
if (sec == null) {
return;
}
for (String name : sec.getKeys(false)) {
ConfigurationSection r = sec.getConfigurationSection(name);
if (r == null) {
continue;
}
regions.put(name.toLowerCase(), new Region(name, r.getString("world", ""),
r.getInt("min-x"), r.getInt("min-z"), r.getInt("max-x"), r.getInt("max-z")));
}
}
private synchronized void save() {
YamlConfiguration y = new YamlConfiguration();
for (Region r : regions.values()) {
String base = "regions." + r.name() + ".";
y.set(base + "world", r.world());
y.set(base + "min-x", r.minX());
y.set(base + "min-z", r.minZ());
y.set(base + "max-x", r.maxX());
y.set(base + "max-z", r.maxZ());
}
try {
y.save(file);
} catch (IOException e) {
log.warning("pvp-regions.yml konnte nicht gespeichert werden: " + e.getMessage());
}
}
// ------------------------------------------------------------ Verwaltung
void setPos1(Player p) {
pos1.put(p.getUniqueId(), p.getLocation());
}
void setPos2(Player p) {
pos2.put(p.getUniqueId(), p.getLocation());
}
/** @return null bei Erfolg, sonst Fehlertext */
String create(Player p, String name) {
Location a = pos1.get(p.getUniqueId());
Location b = pos2.get(p.getUniqueId());
if (a == null || b == null) {
return "Setze zuerst &e/chunkinfo pvp pos1 &7und &e/chunkinfo pvp pos2&7.";
}
if (a.getWorld() == null || !a.getWorld().equals(b.getWorld())) {
return "Beide Punkte muessen in derselben Welt liegen.";
}
regions.put(name.toLowerCase(), new Region(name, a.getWorld().getName(),
Math.min(a.getBlockX(), b.getBlockX()), Math.min(a.getBlockZ(), b.getBlockZ()),
Math.max(a.getBlockX(), b.getBlockX()), Math.max(a.getBlockZ(), b.getBlockZ())));
save();
return null;
}
boolean delete(String name) {
boolean removed = regions.remove(name.toLowerCase()) != null;
if (removed) {
save();
}
return removed;
}
Map<String, Region> all() {
return Collections.unmodifiableMap(regions);
}
Set<String> names() {
return regions.values().stream().map(Region::name).collect(java.util.stream.Collectors.toSet());
}
boolean inRegion(Location l) {
for (Region r : regions.values()) {
if (r.contains(l)) {
return true;
}
}
return false;
}
// ----------------------------------------------------------------- Event
/**
* Laeuft nach Lands (HIGHEST): Wenn Angreifer und Opfer beide in einer PvP-Region stehen,
* wird ein von Lands abgebrochener Schaden wieder freigegeben.
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onDamage(EntityDamageByEntityEvent e) {
if (!e.isCancelled() || regions.isEmpty() || !(e.getEntity() instanceof Player victim)) {
return;
}
Entity damager = e.getDamager();
if (damager instanceof Projectile proj && proj.getShooter() instanceof Entity shooter) {
damager = shooter;
}
if (!(damager instanceof Player attacker) || attacker.equals(victim)) {
return;
}
if (inRegion(attacker.getLocation()) && inRegion(victim.getLocation())) {
e.setCancelled(false);
}
}
}