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) ## 2. Installation (5 Schritte)
1. Stoppe den Server (oder lass ihn laufen, ein Neustart ist trotzdem später nötig). 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. 3. Starte den Server neu.
4. Schau in die Konsole. Du solltest `[LandsChunkInfo] Enabling LandsChunkInfo` sehen. Steht dort ein roter Fehler, lies Abschnitt 9. 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). 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. - 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. - 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. - 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"?> <?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" <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"> 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> <modelVersion>4.0.0</modelVersion>
<groupId>de.landschunkinfo</groupId> <groupId>de.landschunkinfo</groupId>
<artifactId>LandsChunkInfo</artifactId> <artifactId>LandsChunkInfo</artifactId>
<version>1.0.0</version> <version>1.0.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<maven.compiler.release>25</maven.compiler.release> <maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<repositories> <repositories>
<repository> <repository>
<id>papermc</id> <id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url> <url>https://repo.papermc.io/repository/maven-public/</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId> <artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version> <version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>me.angeschossen</groupId> <groupId>me.angeschossen</groupId>
<artifactId>lands</artifactId> <artifactId>lands</artifactId>
<version>8.8.0</version> <version>8.8.0</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/libs/Lands-8.8.0.jar</systemPath> <systemPath>${project.basedir}/libs/Lands-8.8.0.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.milkbowl</groupId> <groupId>net.milkbowl</groupId>
<artifactId>vaultapi</artifactId> <artifactId>vaultapi</artifactId>
<version>1.7.1</version> <version>1.7.1</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/libs/VaultAPI-1.7.1.jar</systemPath> <systemPath>${project.basedir}/libs/VaultAPI-1.7.1.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>me.clip</groupId> <groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId> <artifactId>placeholderapi</artifactId>
<version>2.11.7</version> <version>2.11.7</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/libs/placeholderapi-2.11.7.jar</systemPath> <systemPath>${project.basedir}/libs/placeholderapi-2.11.7.jar</systemPath>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>LandsChunkInfo-${project.version}</finalName> <finalName>LandsChunkInfo-${project.version}</finalName>
<resources> <resources>
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
<filtering>true</filtering> <filtering>true</filtering>
</resource> </resource>
</resources> </resources>
</build> </build>
</project> </project>
@@ -21,7 +21,7 @@ import java.util.UUID;
final class InfoCommand implements CommandExecutor, TabCompleter { 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; private final LandsChunkInfo plugin;
@@ -58,11 +58,52 @@ final class InfoCommand implements CommandExecutor, TabCompleter {
case "queue" -> queue(p, args); case "queue" -> queue(p, args);
case "watch" -> watch(p, args); case "watch" -> watch(p, args);
case "temp" -> temp(p, args); case "temp" -> temp(p, args);
case "pvp" -> pvp(p, args);
default -> info(p, args); default -> info(p, args);
} }
return true; 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) { private boolean allowed(Player p, String perm) {
if (p.hasPermission("landschunkinfo." + perm)) { if (p.hasPermission("landschunkinfo." + perm)) {
return true; return true;
@@ -467,6 +508,13 @@ final class InfoCommand implements CommandExecutor, TabCompleter {
switch (args[0].toLowerCase(Locale.ROOT)) { switch (args[0].toLowerCase(Locale.ROOT)) {
case "queue", "watch" -> out.addAll(args.length == 2 ? List.of("list", "clear") : List.of()); 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 "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" -> { case "skip", "gift" -> {
out.add("confirm"); out.add("confirm");
plugin.landsOf(p.getUniqueId()).forEach(l -> out.add(l.getName())); plugin.landsOf(p.getUniqueId()).forEach(l -> out.add(l.getName()));
@@ -49,6 +49,7 @@ public final class LandsChunkInfo extends JavaPlugin implements Listener {
LandsIntegration lands; LandsIntegration lands;
LockStore store; LockStore store;
VaultHook vault; VaultHook vault;
PvpRegions pvp;
private long cooldownMillis; private long cooldownMillis;
private long lastTick = System.currentTimeMillis(); private long lastTick = System.currentTimeMillis();
@@ -72,6 +73,8 @@ public final class LandsChunkInfo extends JavaPlugin implements Listener {
new ChunkExpansion(this).register(); new ChunkExpansion(this).register();
} }
pvp = new PvpRegions(getDataFolder(), getLogger());
Bukkit.getPluginManager().registerEvents(pvp, this);
Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(this, this);
InfoCommand command = new InfoCommand(this); InfoCommand command = new InfoCommand(this);
var cmd = getCommand("chunkinfo"); 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);
}
}
}