Upload via GUI (14 Dateien)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.landschunkinfo</groupId>
|
||||
<artifactId>LandsChunkInfo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.0.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user