Become independent from BattleArena
This commit is contained in:
1 parent
c9f1685786
commit
e664104257
55 files changed
+4017
-2755
No files matched your search
+11
-13
@@ -1,16 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.1-SNAPSHOT:
|
||||
## 2.0.1-SNAPSHOT
|
||||
|
||||
- First beta-ready snapshot of the Spleef-OG fork. Targets Purpur / Paper 1.19.4.
|
||||
- **Mode scope**: Spleef-OG now supports only Classic Spleef and Bow Spleef. Unsupported projectile-cannon and TNT Run code paths and templates were removed.
|
||||
- **Classic snowballs**: Breaking configured snow floor layers now grants throwable snowballs, and thrown snowballs break configured Spleef layer blocks.
|
||||
- **WorldGuard region flow**: Arenas are intended to run in WorldGuard regions in the main spawn world. The default `world-whitelist` now targets `world`, and `/spleef worldguardregion <map> <region>` stores the map region ID.
|
||||
- **Region whitelist**: `config.yml` now supports `region-whitelist` for limiting Spleef arena creation, joining, resets, floor breaking, projectile floor breaking, and Bow Spleef TNT handling to specific WorldGuard regions.
|
||||
- **Editor layer lookup**: `SpleefMap.positionToLayers` is now maintained on `addLayer`, `removeLayer`, and `clearLayers`. Layers created via `/spleef layer add` are immediately usable; no server restart required.
|
||||
- **NPE on layer removal**: `SpleefMap.removeLayer` and `clearLayers` null-guard `this.layers`, matching the lazy init in `addLayer`. `/spleef layer clear` on a fresh map no longer throws.
|
||||
- **Bow Spleef projectiles**: Arrows break layer blocks in bow-spleef mode.
|
||||
- **`SpleefConfig.getShovel` NPE**: Now null-safe when the configured shovel name is unknown, honoring the `@Nullable` contract. Prevents crash on typos in arena YAML (e.g. `give-shovel{shovel=clasic}`).
|
||||
- **PlayerMoveEvent hot path**: `SpleefArena.onMove` now returns early when `from` and `to` share block coordinates, cutting per-tick overhead for death-region checks by a large margin.
|
||||
- **Scheduler cleanup on disable**: `ArenaSpleef.onDisable` cancels all scheduler tasks owned by the plugin, preventing delayed tasks from firing against stale map references after reload.
|
||||
- Gradle build updated to use the TrueOG bootstrap maven-local repository when available, falling back to `~/.m2`.
|
||||
- Replaced the external arena framework with a native WorldGuard-region arena engine.
|
||||
- Added persistent arena definitions, player recovery snapshots, and Spleef-owned statistics.
|
||||
- Added multiple concurrent Classic and Bow Spleef arenas in whitelisted worlds.
|
||||
- Added a 20-second automatic countdown once at least two players are waiting.
|
||||
- Added Duels-style region setup commands for waiting, spectator, and player spawns.
|
||||
- Added click-based layer and death-region editors.
|
||||
- Added inventory, location, gamemode, health, experience, effect, flight, and scoreboard restoration.
|
||||
- Added waiting, countdown, match, victory, spectator, floor reset, and restart recovery lifecycles.
|
||||
- Renamed the plugin and API implementation packages to `net.trueog.spleefog`.
|
||||
- Kept GameModeInventories-OG, HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, and BattleTracker integrations.
|
||||
@@ -1,121 +1,118 @@
|
||||
# Spleef-OG
|
||||
|
||||
A BattleArena-backed Spleef plugin for Purpur / Paper 1.19.4.
|
||||
A standalone Classic and Bow Spleef plugin for Purpur / Paper 1.19.4.
|
||||
|
||||
Spleef-OG is maintained for TrueOG-style server-spawn minigames. It runs arenas in WorldGuard regions in the main world, not in MyWorlds world copies.
|
||||
Spleef-OG runs multiple concurrent arenas in WorldGuard regions inside normal or MyWorlds-managed worlds. It does not create or copy worlds.
|
||||
|
||||
## Supported Modes
|
||||
- **Classic Spleef**: players receive a shovel and break configured snow floor layers. Breaking `snow_block` floors grants 4 snowballs; breaking `snow` layers grants snowballs equal to the layer count. Thrown snowballs break configured Spleef layer blocks.
|
||||
- **Bow Spleef**: players receive a bow and arrow. Arrows break configured layer blocks, and TNT projectile handling is gated by a configured WorldGuard region.
|
||||
## Gameplay
|
||||
|
||||
For example configurations, see the [templates](./templates) folder.
|
||||
- Classic Spleef gives each player a shovel. Broken snow floors award snowballs, and thrown snowballs break configured floor blocks.
|
||||
- Bow Spleef gives each player an infinity flame bow and one arrow. Arrow impacts break configured floor blocks.
|
||||
- An arena starts a 20-second countdown as soon as at least two players are waiting. The countdown pauses if fewer than two remain.
|
||||
- The last surviving player wins. Matches draw when the configured time limit expires.
|
||||
- Waiting players, active players, and spectators have their inventory, location, gamemode, health, experience, effects, flight state, and scoreboard restored when they leave.
|
||||
- Recovery snapshots survive plugin or server restarts.
|
||||
|
||||
## Region Model
|
||||
- Spleef arenas are intended to live in WorldGuard regions in the main `world` near server spawn.
|
||||
- `config.yml` defaults `world-whitelist` to `world`. Add your real spawn-world name there if it differs.
|
||||
- `region-whitelist` can restrict Spleef to specific WorldGuard region IDs. Leave it empty to allow any region in an allowed world.
|
||||
- Arena creation requires standing in a WorldGuard region with `allow-spleef` set to `allow`.
|
||||
- Maps can set `worldguard-region` to their region ID. Bow Spleef maps should set it to the region that covers the TNT/layer area.
|
||||
- When `region-whitelist` is set, joins, layer resets, block breaks, snowball/arrow block hits, and Bow Spleef TNT handling are limited to whitelisted regions.
|
||||
- Spleef-OG does not depend on MyWorlds and does not create per-match world copies.
|
||||
Spleef statistics are stored in `plugins/Spleef-OG/stats.yml`. Use `/spleef stats [player]` to see wins, losses, ties, and games played.
|
||||
|
||||
Example setup flow:
|
||||
```text
|
||||
/rg define spleef_classic
|
||||
/rg flag spleef_classic allow-spleef allow
|
||||
/spleef create classic
|
||||
/spleef layer add classic
|
||||
/spleef deathregion classic
|
||||
```
|
||||
## Requirements
|
||||
|
||||
For Bow Spleef, also run:
|
||||
```text
|
||||
/spleef worldguardregion bowspleef spleef_bow
|
||||
```
|
||||
- Java 17+
|
||||
- Purpur / Paper 1.19.4
|
||||
- WorldGuard 7
|
||||
- MyWorlds is optional. Declaring it as a soft dependency ensures managed worlds load before arena locations are read.
|
||||
|
||||
## Configuration
|
||||
|
||||
`config.yml` defaults to one allowed world:
|
||||
|
||||
Example `config.yml` whitelist:
|
||||
```yaml
|
||||
world-whitelist:
|
||||
- world
|
||||
region-whitelist:
|
||||
- spleef_classic
|
||||
- spleef_bow
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
- **Server**: Purpur / Paper 1.19.4
|
||||
- **Java**: 17+
|
||||
- **Depends**: BattleArena 4.0.0-SNAPSHOT, WorldGuard
|
||||
- **Soft-depends**: GameModeInventories-OG, HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit
|
||||
It also controls the minimum player count, 20-second waiting period, match time limit, victory delay, tools, and scoreboard.
|
||||
|
||||
## Arena Setup
|
||||
|
||||
Create a WorldGuard region and allow Spleef in it:
|
||||
|
||||
```text
|
||||
/rg define spleef_classic
|
||||
/rg flag spleef_classic allow-spleef allow
|
||||
```
|
||||
|
||||
Stand inside the region, then configure the arena:
|
||||
|
||||
```text
|
||||
/spleef create classic classic
|
||||
/spleef setwait classic
|
||||
/spleef setspectator classic
|
||||
/spleef addspawn classic
|
||||
/spleef addspawn classic
|
||||
/spleef layer add classic
|
||||
/spleef deathregion classic
|
||||
/spleef info classic
|
||||
```
|
||||
|
||||
`layer add` and `deathregion` prompt for two block clicks. Layer setup then prompts for Bukkit block data such as `minecraft:snow_block`. Arena definitions are stored in `plugins/Spleef-OG/arenas.yml`.
|
||||
|
||||
Use `bow` instead of `classic` in the create or mode command for Bow Spleef.
|
||||
|
||||
## Player Commands
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `/spleef arenas` | Show currently available arenas and their join commands. |
|
||||
| `/spleef join` | Show currently available arenas. |
|
||||
| `/spleef join <arena>` | Join a named arena. |
|
||||
| `/spleef leave` | Leave the current arena or spectator session. |
|
||||
| `/spleef spectate <arena>` | Spectate an arena. |
|
||||
| `/spleef stats [player]` | Show Spleef statistics. |
|
||||
|
||||
## Administration
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `/spleef create <name> [classic\|bow]` | Bind a new arena to the smallest WorldGuard region containing you. |
|
||||
| `/spleef delete <arena>` | Delete an inactive arena. |
|
||||
| `/spleef setwait <arena>` | Set its waiting spawn. |
|
||||
| `/spleef setspectator <arena>` | Set its spectator spawn. |
|
||||
| `/spleef addspawn <arena>` | Add a player spawn. |
|
||||
| `/spleef clearspawns <arena>` | Remove all player spawns. |
|
||||
| `/spleef layer <add\|remove\|list\|clear> <arena> [index]` | Manage floor layers. |
|
||||
| `/spleef deathregion <arena>` | Select the elimination volume. |
|
||||
| `/spleef mode <arena> <classic\|bow>` | Change game mode. |
|
||||
| `/spleef enable <arena>` | Enable an arena. |
|
||||
| `/spleef disable <arena>` | Disable an inactive arena. |
|
||||
| `/spleef info <arena>` | Show setup and runtime state. |
|
||||
| `/spleef reload` | Reload `config.yml`. |
|
||||
|
||||
Player permissions are `spleef.play`, `spleef.spectate`, and `spleef.stats`. Administration uses `spleef.admin`.
|
||||
|
||||
## Integrations
|
||||
- **PetTeleport-OG**: pet teleports are suppressed while a player is in a Spleef match. No configuration required; PetTeleport-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **HorseTp-OG**: horse teleports are suppressed while a player is in a Spleef match. No configuration required; HorseTp-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **PlayerBounties-OG**: bounty claims and new bounty placements are cancelled whenever the claimant, victim, or target is in a Spleef match.
|
||||
- **GameModeInventories-OG**: gamemode-specific inventories are preserved while players enter and leave Spleef.
|
||||
|
||||
## Commands
|
||||
| Command | Description |
|
||||
|-----------------------------------------------|-----------------------------------------------------|
|
||||
| `/spleef deathregion <map>` | Sets the death region for a Spleef arena. |
|
||||
| `/spleef layer add <map>` | Adds a floor layer to a Spleef arena. |
|
||||
| `/spleef layer remove <map> <index>` | Removes a layer from a Spleef arena. |
|
||||
| `/spleef layer clear <map>` | Clears all layers from a Spleef arena. |
|
||||
| `/spleef layer index <map> <from> <to>` | Changes the index of a layer. |
|
||||
| `/spleef layer list <map>` | Lists all layers in a Spleef arena. |
|
||||
| `/spleef worldguardregion <map> <region>` | Sets the map WorldGuard region ID. |
|
||||
|
||||
## Permissions
|
||||
| Permission | Command |
|
||||
|----------------------------------------------|---------------------------------|
|
||||
| `battlearena.command.spleef.deathregion` | `/spleef deathregion` |
|
||||
| `battlearena.command.spleef.layer.add` | `/spleef layer add` |
|
||||
| `battlearena.command.spleef.layer.remove` | `/spleef layer remove` |
|
||||
| `battlearena.command.spleef.layer.clear` | `/spleef layer clear` |
|
||||
| `battlearena.command.spleef.layer.index` | `/spleef layer index` |
|
||||
| `battlearena.command.spleef.layer.list` | `/spleef layer list` |
|
||||
| `battlearena.command.spleef.worldguardregion` | `/spleef worldguardregion` |
|
||||
- BattleTracker combat tags prevent joining. Duels and Spleef activity are excluded from BattleTracker's SMP statistics.
|
||||
- GameModeInventories-OG is suspended while Spleef owns a player's temporary inventory.
|
||||
- HorseTp-OG and PetTeleport-OG suppress teleports while `SpleefAPI.isInSpleef` is true.
|
||||
- PlayerBounties-OG claims and placements involving Spleef players are cancelled.
|
||||
|
||||
## API
|
||||
Spleef-OG exposes a small Bukkit-native API for other plugins. No BattleArena types on the surface, only `org.bukkit.entity.Player`.
|
||||
|
||||
### Check if a player is in a Spleef match
|
||||
Java:
|
||||
```java
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI;
|
||||
import net.trueog.spleefog.api.SpleefAPI;
|
||||
|
||||
if (SpleefAPI.isInSpleef(player)) {
|
||||
// ...
|
||||
// Player is waiting, playing, or spectating.
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI
|
||||
|
||||
if (SpleefAPI.isInSpleef(player)) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Events
|
||||
Both extend `org.bukkit.event.player.PlayerEvent`.
|
||||
|
||||
| Event | Fires when | `SpleefAPI.isInSpleef(player)` during event |
|
||||
|--------------------|-------------------------------------------|---------------------------------------------|
|
||||
| `SpleefJoinEvent` | Player joins any Spleef-mode competition | `true` |
|
||||
| `SpleefLeaveEvent` | Player leaves any Spleef-mode competition | `false` |
|
||||
|
||||
Covers Classic Spleef and Bow Spleef. State flips before the event fires, so listeners observe the post-transition value.
|
||||
`SpleefJoinEvent` and `SpleefLeaveEvent` are available in the same package.
|
||||
|
||||
## Building
|
||||
|
||||
```text
|
||||
./gradlew build
|
||||
```
|
||||
Output: `build/libs/Spleef-OG-<version>.jar`
|
||||
|
||||
## Credits
|
||||
Upstream: [BattlePlugins/ArenaSpleef](https://github.com/BattlePlugins/ArenaSpleef). Documentation: [BattleDocs](https://docs.battleplugins.org/books/additional-gamemodes/chapter/spleef).
|
||||
|
||||
## License
|
||||
Released into the public domain. See `LICENSE`.
|
||||
The jar is written to `build/libs`.
|
||||
+1
-4
@@ -17,7 +17,7 @@ java {
|
||||
}
|
||||
|
||||
/* ----------------------------- Metadata ------------------------------ */
|
||||
group = "org.battleplugins.arena"
|
||||
group = "net.trueog"
|
||||
|
||||
version = "2.0.1-SNAPSHOT"
|
||||
|
||||
@@ -37,8 +37,6 @@ repositories {
|
||||
gradlePluginPortal()
|
||||
maven("https://maven.enginehub.org/repo/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.battleplugins.org/releases/")
|
||||
maven("https://repo.battleplugins.org/snapshots/")
|
||||
maven { url = uri("file://${System.getProperty("user.home")}/.m2/repository") }
|
||||
System.getProperty("SELF_MAVEN_LOCAL_REPO")?.let {
|
||||
val dir = file(it)
|
||||
@@ -55,7 +53,6 @@ repositories {
|
||||
/* ---------------------------- Dependencies --------------------------- */
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly("org.battleplugins:arena:4.0.0-SNAPSHOT")
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.8") // Import WorldGuard API.
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.battleplugins.org/releases/")
|
||||
maven("https://repo.battleplugins.org/snapshots/")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.trueog.spleefog;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public final class Messages {
|
||||
|
||||
private static final String PREFIX = "&8[&bSpleef&8] &r";
|
||||
|
||||
private Messages() {
|
||||
|
||||
}
|
||||
|
||||
public static void send(CommandSender sender, String message) {
|
||||
|
||||
sender.sendMessage(SpleefConfig.color(PREFIX + message));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package net.trueog.spleefog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
public final class SpleefConfig {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
|
||||
public SpleefConfig(SpleefPlugin plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
|
||||
}
|
||||
|
||||
public List<String> worldWhitelist() {
|
||||
|
||||
return this.plugin.getConfig().getStringList("world-whitelist");
|
||||
|
||||
}
|
||||
|
||||
public boolean isWorldAllowed(String worldName) {
|
||||
|
||||
if (worldName == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
List<String> worlds = this.worldWhitelist();
|
||||
if (worlds.isEmpty()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return worlds.stream().filter(name -> name != null).anyMatch(name -> name.equalsIgnoreCase(worldName));
|
||||
|
||||
}
|
||||
|
||||
public int minimumPlayers() {
|
||||
|
||||
return Math.max(2, this.plugin.getConfig().getInt("match.minimum-players", 2));
|
||||
|
||||
}
|
||||
|
||||
public int waitingSeconds() {
|
||||
|
||||
return Math.max(1, this.plugin.getConfig().getInt("match.waiting-seconds", 20));
|
||||
|
||||
}
|
||||
|
||||
public int timeLimitSeconds() {
|
||||
|
||||
return Math.max(30, this.plugin.getConfig().getInt("match.time-limit-seconds", 300));
|
||||
|
||||
}
|
||||
|
||||
public int victorySeconds() {
|
||||
|
||||
return Math.max(1, this.plugin.getConfig().getInt("match.victory-seconds", 5));
|
||||
|
||||
}
|
||||
|
||||
public boolean scoreboardEnabled() {
|
||||
|
||||
return this.plugin.getConfig().getBoolean("scoreboard.enabled", true);
|
||||
|
||||
}
|
||||
|
||||
public String scoreboardTitle() {
|
||||
|
||||
return color(this.plugin.getConfig().getString("scoreboard.title", "&b&lSPLEEF"));
|
||||
|
||||
}
|
||||
|
||||
public ItemStack classicTool() {
|
||||
|
||||
return this.readItem("items.classic", Material.DIAMOND_SHOVEL);
|
||||
|
||||
}
|
||||
|
||||
public ItemStack bowTool() {
|
||||
|
||||
return this.readItem("items.bow", Material.BOW);
|
||||
|
||||
}
|
||||
|
||||
private ItemStack readItem(String path, Material fallback) {
|
||||
|
||||
ConfigurationSection section = this.plugin.getConfig().getConfigurationSection(path);
|
||||
if (section == null) {
|
||||
|
||||
return new ItemStack(fallback);
|
||||
|
||||
}
|
||||
|
||||
Material material = Material.matchMaterial(section.getString("material", fallback.name()));
|
||||
ItemStack item = new ItemStack(material == null ? fallback : material);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
String name = section.getString("name");
|
||||
if (name != null) {
|
||||
|
||||
meta.setDisplayName(color(name));
|
||||
|
||||
}
|
||||
|
||||
ConfigurationSection enchantments = section.getConfigurationSection("enchantments");
|
||||
if (enchantments != null) {
|
||||
|
||||
for (String key : enchantments.getKeys(false)) {
|
||||
|
||||
Enchantment enchantment = Enchantment.getByName(key.toUpperCase(Locale.ROOT));
|
||||
if (enchantment != null) {
|
||||
|
||||
meta.addEnchant(enchantment, enchantments.getInt(key), true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static String color(String message) {
|
||||
|
||||
return ChatColor.translateAlternateColorCodes('&', message == null ? "" : message);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package net.trueog.spleefog;
|
||||
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.registry.FlagConflictException;
|
||||
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
|
||||
|
||||
public final class SpleefFlags {
|
||||
|
||||
public static final String ALLOW_SPLEEF = "allow-spleef";
|
||||
private static StateFlag allowSpleefFlag;
|
||||
|
||||
private SpleefFlags() {
|
||||
|
||||
}
|
||||
|
||||
public static void register(SpleefPlugin plugin) {
|
||||
|
||||
FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry();
|
||||
try {
|
||||
|
||||
StateFlag flag = new StateFlag(ALLOW_SPLEEF, false);
|
||||
registry.register(flag);
|
||||
allowSpleefFlag = flag;
|
||||
|
||||
} catch (FlagConflictException conflict) {
|
||||
|
||||
Flag<?> existing = registry.get(ALLOW_SPLEEF);
|
||||
if (existing instanceof StateFlag stateFlag) {
|
||||
|
||||
allowSpleefFlag = stateFlag;
|
||||
|
||||
} else {
|
||||
|
||||
plugin.getLogger().severe("WorldGuard flag '" + ALLOW_SPLEEF + "' has an incompatible type.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static StateFlag allowSpleef() {
|
||||
|
||||
return allowSpleefFlag;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.trueog.spleefog;
|
||||
|
||||
import net.trueog.spleefog.api.SpleefAPI;
|
||||
import net.trueog.spleefog.arena.ArenaManager;
|
||||
import net.trueog.spleefog.command.SpleefCommand;
|
||||
import net.trueog.spleefog.data.ArenaRepository;
|
||||
import net.trueog.spleefog.data.RecoveryStore;
|
||||
import net.trueog.spleefog.data.StatsRepository;
|
||||
import net.trueog.spleefog.editor.ArenaEditor;
|
||||
import net.trueog.spleefog.hook.GameModeInventoriesHook;
|
||||
import net.trueog.spleefog.hook.PlayerBountiesHook;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class SpleefPlugin extends JavaPlugin {
|
||||
|
||||
private ArenaManager arenaManager;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
|
||||
SpleefFlags.register(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
this.saveDefaultConfig();
|
||||
|
||||
SpleefConfig config = new SpleefConfig(this);
|
||||
WorldGuardSupport worldGuard = new WorldGuardSupport();
|
||||
ArenaRepository arenaRepository = new ArenaRepository(this);
|
||||
StatsRepository stats = new StatsRepository(this);
|
||||
RecoveryStore recovery = new RecoveryStore(this);
|
||||
GameModeInventoriesHook gameModeInventories = new GameModeInventoriesHook(this);
|
||||
this.arenaManager = new ArenaManager(this, config, worldGuard, arenaRepository, stats, recovery,
|
||||
gameModeInventories);
|
||||
|
||||
ArenaEditor editor = new ArenaEditor(this, this.arenaManager);
|
||||
this.getServer().getPluginManager().registerEvents(editor, this);
|
||||
SpleefCommand commandHandler = new SpleefCommand(this, this.arenaManager, editor);
|
||||
PluginCommand command = this.getCommand("spleef");
|
||||
if (command == null) {
|
||||
|
||||
throw new IllegalStateException("The spleef command is missing from plugin.yml");
|
||||
|
||||
}
|
||||
|
||||
command.setExecutor(commandHandler);
|
||||
command.setTabCompleter(commandHandler);
|
||||
|
||||
this.arenaManager.start();
|
||||
PlayerBountiesHook.register(this);
|
||||
this.getLogger().info("Loaded " + this.arenaManager.sessions().size() + " standalone Spleef arena(s).");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
if (this.arenaManager != null) {
|
||||
|
||||
this.arenaManager.shutdown();
|
||||
|
||||
}
|
||||
|
||||
SpleefAPI.clear();
|
||||
|
||||
}
|
||||
|
||||
public ArenaManager getArenaManager() {
|
||||
|
||||
return this.arenaManager;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.trueog.spleefog;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import java.util.Comparator;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import net.trueog.spleefog.model.BlockBounds;
|
||||
|
||||
public final class WorldGuardSupport {
|
||||
|
||||
public RegionBinding findArenaRegion(Player player) {
|
||||
|
||||
Location location = player.getLocation();
|
||||
RegionManager manager = this.manager(location.getWorld());
|
||||
if (manager == null || SpleefFlags.allowSpleef() == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
BlockVector3 point = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
ApplicableRegionSet applicable = manager.getApplicableRegions(point);
|
||||
return applicable.getRegions().stream()
|
||||
.filter(region -> !region.getId().equalsIgnoreCase(ProtectedRegion.GLOBAL_REGION))
|
||||
.filter(region -> region.getFlag(SpleefFlags.allowSpleef()) == StateFlag.State.ALLOW).map(this::binding)
|
||||
.min(Comparator.comparingLong(binding -> binding.bounds().volume())).orElse(null);
|
||||
|
||||
}
|
||||
|
||||
public boolean regionExists(World world, String regionId) {
|
||||
|
||||
RegionManager manager = this.manager(world);
|
||||
return manager != null && manager.getRegion(regionId) != null;
|
||||
|
||||
}
|
||||
|
||||
public boolean isInside(Location location, String regionId) {
|
||||
|
||||
if (location == null || location.getWorld() == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
RegionManager manager = this.manager(location.getWorld());
|
||||
ProtectedRegion region = manager == null ? null : manager.getRegion(regionId);
|
||||
return region != null && region.contains(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
|
||||
}
|
||||
|
||||
public BlockBounds regionBounds(World world, String regionId) {
|
||||
|
||||
RegionManager manager = this.manager(world);
|
||||
ProtectedRegion region = manager == null ? null : manager.getRegion(regionId);
|
||||
return region == null ? null : this.binding(region).bounds();
|
||||
|
||||
}
|
||||
|
||||
private RegionManager manager(World world) {
|
||||
|
||||
return world == null ? null
|
||||
: WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
|
||||
|
||||
}
|
||||
|
||||
private RegionBinding binding(ProtectedRegion region) {
|
||||
|
||||
BlockVector3 min = region.getMinimumPoint();
|
||||
BlockVector3 max = region.getMaximumPoint();
|
||||
return new RegionBinding(region.getId(),
|
||||
new BlockBounds(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()));
|
||||
|
||||
}
|
||||
|
||||
public record RegionBinding(String id, BlockBounds bounds) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.trueog.spleefog.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class SpleefAPI {
|
||||
|
||||
private static final Set<UUID> ACTIVE_PLAYERS = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private SpleefAPI() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isInSpleef(Player player) {
|
||||
|
||||
return player != null && ACTIVE_PLAYERS.contains(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
public static boolean markJoined(Player player) {
|
||||
|
||||
return player != null && ACTIVE_PLAYERS.add(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
public static boolean markLeft(Player player) {
|
||||
|
||||
return player != null && ACTIVE_PLAYERS.remove(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
|
||||
ACTIVE_PLAYERS.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.trueog.spleefog.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class SpleefJoinEvent extends PlayerEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
private final String arenaName;
|
||||
private final boolean spectator;
|
||||
|
||||
public SpleefJoinEvent(Player player, String arenaName, boolean spectator) {
|
||||
|
||||
super(player);
|
||||
this.arenaName = arenaName;
|
||||
this.spectator = spectator;
|
||||
|
||||
}
|
||||
|
||||
public String getArenaName() {
|
||||
|
||||
return this.arenaName;
|
||||
|
||||
}
|
||||
|
||||
public boolean isSpectator() {
|
||||
|
||||
return this.spectator;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
|
||||
return HANDLERS;
|
||||
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
|
||||
return HANDLERS;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+12
-5
@@ -1,23 +1,30 @@
|
||||
package org.battleplugins.arena.spleef.api;
|
||||
package net.trueog.spleefog.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SpleefLeaveEvent extends PlayerEvent {
|
||||
public final class SpleefLeaveEvent extends PlayerEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
private final String arenaName;
|
||||
|
||||
public SpleefLeaveEvent(Player player) {
|
||||
public SpleefLeaveEvent(Player player, String arenaName) {
|
||||
|
||||
super(player);
|
||||
this.arenaName = arenaName;
|
||||
|
||||
}
|
||||
|
||||
public String getArenaName() {
|
||||
|
||||
return this.arenaName;
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
|
||||
return HANDLERS;
|
||||
|
||||
@@ -0,0 +1,857 @@
|
||||
package net.trueog.spleefog.arena;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.Messages;
|
||||
import net.trueog.spleefog.SpleefConfig;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.WorldGuardSupport;
|
||||
import net.trueog.spleefog.api.SpleefAPI;
|
||||
import net.trueog.spleefog.api.SpleefJoinEvent;
|
||||
import net.trueog.spleefog.api.SpleefLeaveEvent;
|
||||
import net.trueog.spleefog.data.ArenaRepository;
|
||||
import net.trueog.spleefog.data.RecoveryStore;
|
||||
import net.trueog.spleefog.data.StatsRepository;
|
||||
import net.trueog.spleefog.hook.GameModeInventoriesHook;
|
||||
import net.trueog.spleefog.hook.BattleTrackerHook;
|
||||
import net.trueog.spleefog.model.ArenaState;
|
||||
import net.trueog.spleefog.model.GameType;
|
||||
import net.trueog.spleefog.model.SpleefArena;
|
||||
import net.trueog.spleefog.model.SpleefLayer;
|
||||
import net.trueog.spleefog.player.PlayerSnapshot;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Snow;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.entity.Snowball;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.block.TNTPrimeEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
import org.bukkit.scoreboard.Criteria;
|
||||
import org.bukkit.scoreboard.DisplaySlot;
|
||||
import org.bukkit.scoreboard.Objective;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
public final class ArenaManager implements Listener {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final SpleefConfig config;
|
||||
private final WorldGuardSupport worldGuard;
|
||||
private final ArenaRepository arenaRepository;
|
||||
private final StatsRepository stats;
|
||||
private final RecoveryStore recovery;
|
||||
private final GameModeInventoriesHook gameModeInventories;
|
||||
private final BattleTrackerHook battleTracker;
|
||||
private final Map<String, ArenaSession> sessions = new HashMap<>();
|
||||
private final Map<UUID, ArenaSession> playerSessions = new HashMap<>();
|
||||
private final Map<UUID, ArenaSession> pendingRespawns = new HashMap<>();
|
||||
private final Set<UUID> forcedDeaths = new HashSet<>();
|
||||
private BukkitTask tickTask;
|
||||
|
||||
public ArenaManager(SpleefPlugin plugin, SpleefConfig config, WorldGuardSupport worldGuard,
|
||||
ArenaRepository arenaRepository, StatsRepository stats, RecoveryStore recovery,
|
||||
GameModeInventoriesHook gameModeInventories)
|
||||
{
|
||||
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.worldGuard = worldGuard;
|
||||
this.arenaRepository = arenaRepository;
|
||||
this.stats = stats;
|
||||
this.recovery = recovery;
|
||||
this.gameModeInventories = gameModeInventories;
|
||||
this.battleTracker = new BattleTrackerHook(plugin);
|
||||
for (SpleefArena arena : arenaRepository.load()) {
|
||||
|
||||
this.sessions.put(normalize(arena.name()), new ArenaSession(this, arena));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(this, this.plugin);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
|
||||
this.restoreRecovery(player);
|
||||
|
||||
}
|
||||
|
||||
for (ArenaSession session : this.sessions.values()) {
|
||||
|
||||
if (this.isArenaRuntimeValid(session.arena())) {
|
||||
|
||||
this.resetLayers(session.arena());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.tickTask = Bukkit.getScheduler().runTaskTimer(this.plugin,
|
||||
() -> new ArrayList<>(this.sessions.values()).forEach(ArenaSession::tick), 20L, 20L);
|
||||
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
||||
if (this.tickTask != null) {
|
||||
|
||||
this.tickTask.cancel();
|
||||
|
||||
}
|
||||
|
||||
for (ArenaSession session : new ArrayList<>(this.sessions.values())) {
|
||||
|
||||
session.shutdown();
|
||||
|
||||
}
|
||||
|
||||
this.playerSessions.clear();
|
||||
this.pendingRespawns.clear();
|
||||
this.forcedDeaths.clear();
|
||||
this.stats.save();
|
||||
this.gameModeInventories.releaseAll();
|
||||
|
||||
}
|
||||
|
||||
public SpleefConfig config() {
|
||||
|
||||
return this.config;
|
||||
|
||||
}
|
||||
|
||||
public StatsRepository stats() {
|
||||
|
||||
return this.stats;
|
||||
|
||||
}
|
||||
|
||||
public ArenaSession session(Player player) {
|
||||
|
||||
return player == null ? null : this.playerSessions.get(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
public ArenaSession get(String name) {
|
||||
|
||||
return name == null ? null : this.sessions.get(normalize(name));
|
||||
|
||||
}
|
||||
|
||||
public boolean isInCombat(Player player) {
|
||||
|
||||
return this.battleTracker.isInCombat(player);
|
||||
|
||||
}
|
||||
|
||||
public List<ArenaSession> sessions() {
|
||||
|
||||
return this.sessions.values().stream().sorted(Comparator.comparing(value -> value.arena().name())).toList();
|
||||
|
||||
}
|
||||
|
||||
public List<ArenaSession> availableSessions() {
|
||||
|
||||
return this.sessions().stream().filter(ArenaSession::canJoin).toList();
|
||||
|
||||
}
|
||||
|
||||
public CreateResult create(Player player, String name, GameType gameType) {
|
||||
|
||||
if (!name.matches("[A-Za-z0-9_-]+")) {
|
||||
|
||||
return new CreateResult(false, "Arena names may contain letters, numbers, underscores, and hyphens.", null);
|
||||
|
||||
}
|
||||
|
||||
if (this.get(name) != null) {
|
||||
|
||||
return new CreateResult(false, "An arena with that name already exists.", null);
|
||||
|
||||
}
|
||||
|
||||
if (!this.config.isWorldAllowed(player.getWorld().getName())) {
|
||||
|
||||
return new CreateResult(false, "World &b" + player.getWorld().getName() + " &7is not whitelisted.", null);
|
||||
|
||||
}
|
||||
|
||||
WorldGuardSupport.RegionBinding region = this.worldGuard.findArenaRegion(player);
|
||||
if (region == null) {
|
||||
|
||||
return new CreateResult(false,
|
||||
"Stand inside a WorldGuard region with &ballow-spleef=allow &7before creating an arena.", null);
|
||||
|
||||
}
|
||||
|
||||
SpleefArena arena = new SpleefArena(name, player.getWorld().getName(), region.id(), region.bounds(), gameType);
|
||||
this.sessions.put(normalize(name), new ArenaSession(this, arena));
|
||||
this.saveArenas();
|
||||
return new CreateResult(true, "Created &b" + name + " &7in region &b" + region.id() + "&7.", arena);
|
||||
|
||||
}
|
||||
|
||||
public boolean delete(SpleefArena arena) {
|
||||
|
||||
ArenaSession session = this.get(arena.name());
|
||||
if (session == null || session.isActive()) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.sessions.remove(normalize(arena.name()));
|
||||
this.saveArenas();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void saveArenas() {
|
||||
|
||||
this.arenaRepository.save(this.sessions.values().stream().map(ArenaSession::arena).toList());
|
||||
|
||||
}
|
||||
|
||||
public boolean isArenaRuntimeValid(SpleefArena arena) {
|
||||
|
||||
World world = Bukkit.getWorld(arena.worldName());
|
||||
return world != null && this.config.isWorldAllowed(arena.worldName())
|
||||
&& this.worldGuard.regionExists(world, arena.regionId());
|
||||
|
||||
}
|
||||
|
||||
public boolean isSetupLocationValid(SpleefArena arena, Location location) {
|
||||
|
||||
return location != null && location.getWorld() != null
|
||||
&& location.getWorld().getName().equalsIgnoreCase(arena.worldName())
|
||||
&& this.worldGuard.isInside(location, arena.regionId());
|
||||
|
||||
}
|
||||
|
||||
boolean enter(Player player, ArenaSession session, boolean spectator, Location destination) {
|
||||
|
||||
if (destination == null || destination.getWorld() == null || this.recovery.contains(player.getUniqueId())) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.recovery.capture(player);
|
||||
this.gameModeInventories.suspend(player);
|
||||
this.playerSessions.put(player.getUniqueId(), session);
|
||||
SpleefAPI.markJoined(player);
|
||||
this.preparePlayer(player, spectator ? GameMode.SPECTATOR : GameMode.ADVENTURE, destination);
|
||||
Bukkit.getPluginManager().callEvent(new SpleefJoinEvent(player, session.arena().name(), spectator));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void exit(Player player, ArenaSession expectedSession) {
|
||||
|
||||
ArenaSession current = this.playerSessions.get(player.getUniqueId());
|
||||
if (current != expectedSession) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.playerSessions.remove(player.getUniqueId());
|
||||
this.pendingRespawns.remove(player.getUniqueId());
|
||||
this.forcedDeaths.remove(player.getUniqueId());
|
||||
SpleefAPI.markLeft(player);
|
||||
Bukkit.getPluginManager().callEvent(new SpleefLeaveEvent(player, expectedSession.arena().name()));
|
||||
|
||||
PlayerSnapshot snapshot = this.recovery.get(player.getUniqueId());
|
||||
if (snapshot != null && !player.isDead()) {
|
||||
|
||||
try {
|
||||
|
||||
snapshot.restore(player);
|
||||
this.recovery.remove(player.getUniqueId());
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
this.plugin.getLogger().log(java.util.logging.Level.SEVERE,
|
||||
"Could not restore " + player.getName() + " after Spleef; recovery data was retained.", ex);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
||||
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.gameModeInventories.release(player));
|
||||
|
||||
}
|
||||
|
||||
void startPlayer(Player player, SpleefArena arena, Location spawn) {
|
||||
|
||||
this.preparePlayer(player, GameMode.SURVIVAL, spawn);
|
||||
if (arena.gameType() == GameType.CLASSIC) {
|
||||
|
||||
player.getInventory().setItem(0, this.config.classicTool());
|
||||
|
||||
} else {
|
||||
|
||||
player.getInventory().setItem(0, this.config.bowTool());
|
||||
player.getInventory().setItem(9, new ItemStack(Material.ARROW));
|
||||
|
||||
}
|
||||
|
||||
player.updateInventory();
|
||||
|
||||
}
|
||||
|
||||
void showEnding(ArenaSession session) {
|
||||
|
||||
for (UUID playerId : session.allPresent()) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null && !player.isDead()) {
|
||||
|
||||
this.preparePlayer(player, GameMode.ADVENTURE, session.arena().waitingSpawn());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void resetLayers(SpleefArena arena) {
|
||||
|
||||
World world = Bukkit.getWorld(arena.worldName());
|
||||
if (world == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (SpleefLayer layer : arena.layers()) {
|
||||
|
||||
for (int x = layer.bounds().minX(); x <= layer.bounds().maxX(); x++) {
|
||||
|
||||
for (int y = layer.bounds().minY(); y <= layer.bounds().maxY(); y++) {
|
||||
|
||||
for (int z = layer.bounds().minZ(); z <= layer.bounds().maxZ(); z++) {
|
||||
|
||||
Location location = new Location(world, x, y, z);
|
||||
if (this.worldGuard.isInside(location, arena.regionId())) {
|
||||
|
||||
world.getBlockAt(x, y, z).setBlockData(layer.blockData(), false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void updateScoreboards(ArenaSession session) {
|
||||
|
||||
if (!this.config.scoreboardEnabled()) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (UUID playerId : session.allPresent()) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
|
||||
player.setScoreboard(this.createScoreboard(session));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Scoreboard createScoreboard(ArenaSession session) {
|
||||
|
||||
Scoreboard board = Bukkit.getScoreboardManager().getNewScoreboard();
|
||||
Objective objective = board.registerNewObjective("spleef", Criteria.DUMMY, this.config.scoreboardTitle());
|
||||
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||
objective.getScore(ChatColor.GRAY + "Arena: " + ChatColor.WHITE + session.arena().name()).setScore(5);
|
||||
objective.getScore(ChatColor.GRAY + "Mode: " + ChatColor.WHITE + display(session.arena().gameType()))
|
||||
.setScore(4);
|
||||
objective.getScore(ChatColor.GRAY + "State: " + ChatColor.WHITE + display(session.state())).setScore(3);
|
||||
objective
|
||||
.getScore(ChatColor.GRAY + "Players: " + ChatColor.AQUA
|
||||
+ (session.state() == ArenaState.IN_GAME ? session.aliveCount() : session.playerCount()))
|
||||
.setScore(2);
|
||||
String timer = session.state() == ArenaState.WAITING ? "Waiting" : formatTime(session.secondsRemaining());
|
||||
objective.getScore(ChatColor.GRAY + "Time: " + ChatColor.AQUA + timer).setScore(1);
|
||||
return board;
|
||||
|
||||
}
|
||||
|
||||
private void preparePlayer(Player player, GameMode gameMode, Location destination) {
|
||||
|
||||
player.closeInventory();
|
||||
player.getInventory().clear();
|
||||
player.getInventory().setArmorContents(new ItemStack[4]);
|
||||
player.getInventory().setItemInOffHand(null);
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
|
||||
player.removePotionEffect(effect.getType());
|
||||
|
||||
}
|
||||
|
||||
player.setFireTicks(0);
|
||||
player.setFallDistance(0.0F);
|
||||
player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
|
||||
player.setFoodLevel(20);
|
||||
player.setSaturation(5.0F);
|
||||
player.setAllowFlight(gameMode == GameMode.SPECTATOR);
|
||||
player.setGameMode(gameMode);
|
||||
player.teleport(destination);
|
||||
|
||||
}
|
||||
|
||||
private void restoreRecovery(Player player) {
|
||||
|
||||
if (this.playerSessions.containsKey(player.getUniqueId())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
PlayerSnapshot snapshot = this.recovery.get(player.getUniqueId());
|
||||
if (snapshot != null) {
|
||||
|
||||
this.gameModeInventories.suspend(player);
|
||||
try {
|
||||
|
||||
snapshot.restore(player);
|
||||
this.recovery.remove(player.getUniqueId());
|
||||
Messages.send(player, "&7Your pre-Spleef state was recovered after a restart.");
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
this.plugin.getLogger().log(java.util.logging.Level.SEVERE,
|
||||
"Could not recover " + player.getName() + " after a restart; recovery data was retained.", ex);
|
||||
|
||||
} finally {
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.gameModeInventories.release(player));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ArenaSession activeSessionAt(Location location) {
|
||||
|
||||
return this.sessions.values().stream().filter(ArenaSession::isActive)
|
||||
.filter(session -> this.worldGuard.isInside(location, session.arena().regionId())).findFirst()
|
||||
.orElse(null);
|
||||
|
||||
}
|
||||
|
||||
private SpleefLayer layerAt(SpleefArena arena, Location location) {
|
||||
|
||||
if (!this.worldGuard.isInside(location, arena.regionId())) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return arena.layers().stream().filter(layer -> layer.bounds().contains(location)).findFirst().orElse(null);
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
|
||||
Player player = event.getPlayer();
|
||||
ArenaSession session = this.session(player);
|
||||
ArenaSession regionSession = this.activeSessionAt(event.getBlock().getLocation());
|
||||
if (session == null && regionSession == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
if (session == null || session.state() != ArenaState.IN_GAME || !session.isAlive(player.getUniqueId())
|
||||
|| session.arena().gameType() != GameType.CLASSIC)
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefLayer layer = this.layerAt(session.arena(), event.getBlock().getLocation());
|
||||
if (layer == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(false);
|
||||
event.setDropItems(false);
|
||||
int snowballs = snowballCount(event.getBlock());
|
||||
if (snowballs > 0) {
|
||||
|
||||
player.getInventory().addItem(new ItemStack(Material.SNOWBALL, snowballs));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
|
||||
if (this.session(event.getPlayer()) != null) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
|
||||
ArenaSession session = this.session(event.getPlayer());
|
||||
if (session != null && session.state() != ArenaState.IN_GAME) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onDrop(PlayerDropItemEvent event) {
|
||||
|
||||
if (this.session(event.getPlayer()) != null) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onPickup(EntityPickupItemEvent event) {
|
||||
|
||||
if (event.getEntity() instanceof Player player && this.session(player) != null) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
|
||||
if (event.getWhoClicked() instanceof Player player && this.session(player) != null) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onDamage(EntityDamageEvent event) {
|
||||
|
||||
if (!(event.getEntity() instanceof Player player) || this.session(player) == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!this.forcedDeaths.contains(player.getUniqueId())) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onFood(FoodLevelChangeEvent event) {
|
||||
|
||||
if (event.getEntity() instanceof Player player && this.session(player) != null) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onMove(PlayerMoveEvent event) {
|
||||
|
||||
if (event.getTo() == null || sameBlock(event.getFrom(), event.getTo())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.session(event.getPlayer());
|
||||
if (session == null || !session.isAlive(event.getPlayer().getUniqueId())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (session.arena().deathRegion().contains(event.getTo())
|
||||
|| !this.worldGuard.isInside(event.getTo(), session.arena().regionId()))
|
||||
{
|
||||
|
||||
this.forcedDeaths.add(event.getPlayer().getUniqueId());
|
||||
event.getPlayer().setHealth(0.0D);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onDeath(PlayerDeathEvent event) {
|
||||
|
||||
Player player = event.getEntity();
|
||||
ArenaSession session = this.session(player);
|
||||
if (session == null || !session.isAlive(player.getUniqueId())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setKeepInventory(true);
|
||||
event.setKeepLevel(true);
|
||||
event.getDrops().clear();
|
||||
event.deathMessage(null);
|
||||
this.forcedDeaths.remove(player.getUniqueId());
|
||||
this.pendingRespawns.put(player.getUniqueId(), session);
|
||||
session.eliminate(player);
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
|
||||
if (player.isOnline() && player.isDead()) {
|
||||
|
||||
player.spigot().respawn();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onRespawn(PlayerRespawnEvent event) {
|
||||
|
||||
ArenaSession session = this.pendingRespawns.remove(event.getPlayer().getUniqueId());
|
||||
if (session == null) {
|
||||
|
||||
if (this.recovery.contains(event.getPlayer().getUniqueId())) {
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.restoreRecovery(event.getPlayer()));
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Location destination = session.state() == ArenaState.IN_GAME ? session.arena().spectatorSpawn()
|
||||
: session.arena().waitingSpawn();
|
||||
event.setRespawnLocation(destination);
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
|
||||
if (this.session(event.getPlayer()) == session) {
|
||||
|
||||
this.preparePlayer(event.getPlayer(),
|
||||
session.state() == ArenaState.IN_GAME ? GameMode.SPECTATOR : GameMode.ADVENTURE, destination);
|
||||
|
||||
} else {
|
||||
|
||||
this.restoreRecovery(event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onProjectileHit(ProjectileHitEvent event) {
|
||||
|
||||
Block block = event.getHitBlock();
|
||||
Projectile projectile = event.getEntity();
|
||||
ProjectileSource source = projectile.getShooter();
|
||||
if (block == null || !(source instanceof Player player)) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.session(player);
|
||||
if (session == null || !session.isAlive(player.getUniqueId()) || session.state() != ArenaState.IN_GAME) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
boolean validProjectile = session.arena().gameType() == GameType.CLASSIC && projectile instanceof Snowball
|
||||
|| session.arena().gameType() == GameType.BOW && projectile instanceof AbstractArrow;
|
||||
if (!validProjectile || this.layerAt(session.arena(), block.getLocation()) == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
block.setType(Material.AIR, false);
|
||||
projectile.remove();
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onTntPrime(TNTPrimeEvent event) {
|
||||
|
||||
ArenaSession session = this.activeSessionAt(event.getBlock().getLocation());
|
||||
if (session == null || session.state() != ArenaState.IN_GAME || session.arena().gameType() != GameType.BOW
|
||||
|| this.layerAt(session.arena(), event.getBlock().getLocation()) == null)
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
event.getBlock().setType(Material.AIR, false);
|
||||
TNTPrimed visual = event.getBlock().getWorld().spawn(event.getBlock().getLocation().toCenterLocation(),
|
||||
TNTPrimed.class, tnt -> tnt.setFuseTicks(Integer.MAX_VALUE));
|
||||
Bukkit.getScheduler().runTaskLater(this.plugin, visual::remove, 20L);
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onExplosion(EntityExplodeEvent event) {
|
||||
|
||||
event.blockList().removeIf(block -> {
|
||||
|
||||
ArenaSession session = this.activeSessionAt(block.getLocation());
|
||||
return session != null && this.layerAt(session.arena(), block.getLocation()) != null;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
|
||||
ArenaSession session = this.session(event.getPlayer());
|
||||
if (session != null) {
|
||||
|
||||
session.leave(event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.restoreRecovery(event.getPlayer()));
|
||||
|
||||
}
|
||||
|
||||
private static int snowballCount(Block block) {
|
||||
|
||||
if (block.getType() == Material.SNOW_BLOCK) {
|
||||
|
||||
return 4;
|
||||
|
||||
}
|
||||
|
||||
BlockData data = block.getBlockData();
|
||||
if (data instanceof Snow snow) {
|
||||
|
||||
return snow.getLayers();
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
private static boolean sameBlock(Location first, Location second) {
|
||||
|
||||
return first.getBlockX() == second.getBlockX() && first.getBlockY() == second.getBlockY()
|
||||
&& first.getBlockZ() == second.getBlockZ();
|
||||
|
||||
}
|
||||
|
||||
private static String formatTime(int seconds) {
|
||||
|
||||
return String.format(Locale.ROOT, "%d:%02d", Math.max(0, seconds) / 60, Math.max(0, seconds) % 60);
|
||||
|
||||
}
|
||||
|
||||
private static String display(Enum<?> value) {
|
||||
|
||||
String name = value.name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return Character.toUpperCase(name.charAt(0)) + name.substring(1);
|
||||
|
||||
}
|
||||
|
||||
private static String normalize(String name) {
|
||||
|
||||
return name.toLowerCase(Locale.ROOT);
|
||||
|
||||
}
|
||||
|
||||
public record CreateResult(boolean success, String message, SpleefArena arena) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
package net.trueog.spleefog.arena;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.Messages;
|
||||
import net.trueog.spleefog.model.ArenaState;
|
||||
import net.trueog.spleefog.model.SpleefArena;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class ArenaSession {
|
||||
|
||||
private final ArenaManager manager;
|
||||
private final SpleefArena arena;
|
||||
private final Set<UUID> players = new LinkedHashSet<>();
|
||||
private final Set<UUID> spectators = new LinkedHashSet<>();
|
||||
private final Set<UUID> alive = new LinkedHashSet<>();
|
||||
private final Set<UUID> matchPlayers = new LinkedHashSet<>();
|
||||
private final Set<UUID> eliminated = new LinkedHashSet<>();
|
||||
private final Map<UUID, String> playerNames = new LinkedHashMap<>();
|
||||
private ArenaState state = ArenaState.WAITING;
|
||||
private int secondsRemaining;
|
||||
|
||||
ArenaSession(ArenaManager manager, SpleefArena arena) {
|
||||
|
||||
this.manager = manager;
|
||||
this.arena = arena;
|
||||
|
||||
}
|
||||
|
||||
public SpleefArena arena() {
|
||||
|
||||
return this.arena;
|
||||
|
||||
}
|
||||
|
||||
public ArenaState state() {
|
||||
|
||||
return this.state;
|
||||
|
||||
}
|
||||
|
||||
public int secondsRemaining() {
|
||||
|
||||
return this.secondsRemaining;
|
||||
|
||||
}
|
||||
|
||||
public int playerCount() {
|
||||
|
||||
return this.players.size();
|
||||
|
||||
}
|
||||
|
||||
public int spectatorCount() {
|
||||
|
||||
return this.spectators.size();
|
||||
|
||||
}
|
||||
|
||||
public int aliveCount() {
|
||||
|
||||
return this.alive.size();
|
||||
|
||||
}
|
||||
|
||||
public boolean hasPlayer(UUID playerId) {
|
||||
|
||||
return this.players.contains(playerId);
|
||||
|
||||
}
|
||||
|
||||
public boolean hasSpectator(UUID playerId) {
|
||||
|
||||
return this.spectators.contains(playerId);
|
||||
|
||||
}
|
||||
|
||||
public boolean isAlive(UUID playerId) {
|
||||
|
||||
return this.state == ArenaState.IN_GAME && this.alive.contains(playerId);
|
||||
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
|
||||
return !this.players.isEmpty() || !this.spectators.isEmpty();
|
||||
|
||||
}
|
||||
|
||||
public boolean canJoin() {
|
||||
|
||||
return this.arena.enabled() && this.arena.isComplete()
|
||||
&& (this.state == ArenaState.WAITING || this.state == ArenaState.COUNTDOWN)
|
||||
&& this.players.size() < this.arena.capacity() && this.manager.isArenaRuntimeValid(this.arena);
|
||||
|
||||
}
|
||||
|
||||
public boolean join(Player player) {
|
||||
|
||||
if (!this.canJoin() || this.manager.session(player) != null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (this.manager.isInCombat(player)) {
|
||||
|
||||
Messages.send(player, "&cYou cannot join Spleef while combat tagged.");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (!this.manager.enter(player, this, false, this.arena.waitingSpawn())) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.players.add(player.getUniqueId());
|
||||
this.playerNames.put(player.getUniqueId(), player.getName());
|
||||
this.broadcast(
|
||||
"&f" + player.getName() + " &7joined. &b(" + this.players.size() + "/" + this.arena.capacity() + ")");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public boolean spectate(Player player) {
|
||||
|
||||
if (!this.arena.enabled() || !this.arena.isComplete() || this.state == ArenaState.ENDING
|
||||
|| this.manager.session(player) != null || !this.manager.isArenaRuntimeValid(this.arena))
|
||||
{
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (!this.manager.enter(player, this, true, this.arena.spectatorSpawn())) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.spectators.add(player.getUniqueId());
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void leave(Player player) {
|
||||
|
||||
UUID playerId = player.getUniqueId();
|
||||
boolean participant = this.players.remove(playerId);
|
||||
boolean spectator = this.spectators.remove(playerId);
|
||||
if (!participant && !spectator) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (participant && this.state == ArenaState.IN_GAME && this.matchPlayers.contains(playerId)) {
|
||||
|
||||
this.alive.remove(playerId);
|
||||
this.eliminated.add(playerId);
|
||||
|
||||
}
|
||||
|
||||
this.manager.exit(player, this);
|
||||
if (participant && (this.state == ArenaState.WAITING || this.state == ArenaState.COUNTDOWN)) {
|
||||
|
||||
this.broadcast(
|
||||
"&f" + player.getName() + " &7left. &b(" + this.players.size() + "/" + this.arena.capacity() + ")");
|
||||
|
||||
}
|
||||
|
||||
if (this.state == ArenaState.IN_GAME) {
|
||||
|
||||
this.checkForWinner();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void eliminate(Player player) {
|
||||
|
||||
UUID playerId = player.getUniqueId();
|
||||
if (!this.alive.remove(playerId)) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.eliminated.add(playerId);
|
||||
this.broadcast("&f" + player.getName() + " &7was eliminated. &b" + this.alive.size() + " &7remain.");
|
||||
this.checkForWinner();
|
||||
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
||||
switch (this.state) {
|
||||
|
||||
case WAITING -> this.tickWaiting();
|
||||
case COUNTDOWN -> this.tickCountdown();
|
||||
case IN_GAME -> this.tickGame();
|
||||
case ENDING -> this.tickEnding();
|
||||
|
||||
}
|
||||
|
||||
this.manager.updateScoreboards(this);
|
||||
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
||||
for (UUID playerId : this.allPresent()) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
|
||||
this.manager.exit(player, this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.clearRuntime();
|
||||
this.manager.resetLayers(this.arena);
|
||||
|
||||
}
|
||||
|
||||
private void tickWaiting() {
|
||||
|
||||
if (this.players.size() < this.manager.config().minimumPlayers()) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.state = ArenaState.COUNTDOWN;
|
||||
this.secondsRemaining = this.manager.config().waitingSeconds();
|
||||
this.broadcast("&7Match starts in &b" + this.secondsRemaining + " seconds&7.");
|
||||
|
||||
}
|
||||
|
||||
private void tickCountdown() {
|
||||
|
||||
if (this.players.size() < this.manager.config().minimumPlayers()) {
|
||||
|
||||
this.state = ArenaState.WAITING;
|
||||
this.secondsRemaining = 0;
|
||||
this.broadcast("&7Countdown paused until at least &b" + this.manager.config().minimumPlayers()
|
||||
+ " players &7are waiting.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (this.secondsRemaining <= 1) {
|
||||
|
||||
this.startMatch();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.secondsRemaining--;
|
||||
if (this.secondsRemaining <= 5 || this.secondsRemaining == 10 || this.secondsRemaining == 15) {
|
||||
|
||||
this.broadcast("&7Match starts in &b" + this.secondsRemaining + " seconds&7.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void startMatch() {
|
||||
|
||||
if (!this.canStart()) {
|
||||
|
||||
this.state = ArenaState.WAITING;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.manager.resetLayers(this.arena);
|
||||
this.state = ArenaState.IN_GAME;
|
||||
this.secondsRemaining = this.manager.config().timeLimitSeconds();
|
||||
this.alive.clear();
|
||||
this.alive.addAll(this.players);
|
||||
this.matchPlayers.clear();
|
||||
this.matchPlayers.addAll(this.players);
|
||||
this.eliminated.clear();
|
||||
|
||||
List<org.bukkit.Location> spawns = this.arena.spawns();
|
||||
int spawnIndex = 0;
|
||||
for (UUID playerId : this.players) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
|
||||
this.manager.startPlayer(player, this.arena, spawns.get(spawnIndex++));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.broadcast("&aGo!");
|
||||
|
||||
}
|
||||
|
||||
private void tickGame() {
|
||||
|
||||
if (this.secondsRemaining <= 1) {
|
||||
|
||||
this.finish(null, true);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.secondsRemaining--;
|
||||
if (this.secondsRemaining <= 10 || this.secondsRemaining == 30 || this.secondsRemaining == 60) {
|
||||
|
||||
this.broadcast("&b" + this.secondsRemaining + " seconds &7remaining.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void tickEnding() {
|
||||
|
||||
if (this.secondsRemaining <= 1) {
|
||||
|
||||
this.resetAfterMatch();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.secondsRemaining--;
|
||||
|
||||
}
|
||||
|
||||
private boolean canStart() {
|
||||
|
||||
return this.players.size() >= this.manager.config().minimumPlayers()
|
||||
&& this.players.size() <= this.arena.capacity() && this.manager.isArenaRuntimeValid(this.arena);
|
||||
|
||||
}
|
||||
|
||||
private void checkForWinner() {
|
||||
|
||||
if (this.state != ArenaState.IN_GAME || this.alive.size() > 1) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
UUID winner = this.alive.stream().findFirst().orElse(null);
|
||||
this.finish(winner, false);
|
||||
|
||||
}
|
||||
|
||||
private void finish(UUID winner, boolean timedOut) {
|
||||
|
||||
if (this.state != ArenaState.IN_GAME) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.state = ArenaState.ENDING;
|
||||
this.secondsRemaining = this.manager.config().victorySeconds();
|
||||
|
||||
for (UUID playerId : this.matchPlayers) {
|
||||
|
||||
String name = this.playerNames.getOrDefault(playerId, "Unknown");
|
||||
if (playerId.equals(winner)) {
|
||||
|
||||
this.manager.stats().recordWin(playerId, name);
|
||||
|
||||
} else if (timedOut && this.alive.contains(playerId)) {
|
||||
|
||||
this.manager.stats().recordTie(playerId, name);
|
||||
|
||||
} else {
|
||||
|
||||
this.manager.stats().recordLoss(playerId, name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.manager.stats().save();
|
||||
|
||||
if (winner != null) {
|
||||
|
||||
String winnerName = this.playerNames.getOrDefault(winner, "Unknown");
|
||||
this.broadcast("&a" + winnerName + " won the match!");
|
||||
|
||||
} else if (timedOut && !this.alive.isEmpty()) {
|
||||
|
||||
this.broadcast("&eTime expired. The match is a draw.");
|
||||
|
||||
} else {
|
||||
|
||||
this.broadcast("&eThe match ended without a winner.");
|
||||
|
||||
}
|
||||
|
||||
this.manager.showEnding(this);
|
||||
|
||||
}
|
||||
|
||||
private void resetAfterMatch() {
|
||||
|
||||
for (UUID playerId : this.allPresent()) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
|
||||
this.manager.exit(player, this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.clearRuntime();
|
||||
this.manager.resetLayers(this.arena);
|
||||
|
||||
}
|
||||
|
||||
private void clearRuntime() {
|
||||
|
||||
this.players.clear();
|
||||
this.spectators.clear();
|
||||
this.alive.clear();
|
||||
this.matchPlayers.clear();
|
||||
this.eliminated.clear();
|
||||
this.playerNames.clear();
|
||||
this.state = ArenaState.WAITING;
|
||||
this.secondsRemaining = 0;
|
||||
|
||||
}
|
||||
|
||||
public Set<UUID> allPresent() {
|
||||
|
||||
Set<UUID> result = new LinkedHashSet<>(this.players);
|
||||
result.addAll(this.spectators);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public Set<UUID> players() {
|
||||
|
||||
return Set.copyOf(this.players);
|
||||
|
||||
}
|
||||
|
||||
public Set<UUID> spectators() {
|
||||
|
||||
return Set.copyOf(this.spectators);
|
||||
|
||||
}
|
||||
|
||||
private void broadcast(String message) {
|
||||
|
||||
for (UUID playerId : this.allPresent()) {
|
||||
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
|
||||
Messages.send(player, message);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
package net.trueog.spleefog.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import net.trueog.spleefog.Messages;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.arena.ArenaManager;
|
||||
import net.trueog.spleefog.arena.ArenaSession;
|
||||
import net.trueog.spleefog.data.StatsRepository.PlayerStats;
|
||||
import net.trueog.spleefog.editor.ArenaEditor;
|
||||
import net.trueog.spleefog.model.GameType;
|
||||
import net.trueog.spleefog.model.SpleefArena;
|
||||
import net.trueog.spleefog.model.SpleefLayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class SpleefCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final ArenaManager arenaManager;
|
||||
private final ArenaEditor editor;
|
||||
|
||||
public SpleefCommand(SpleefPlugin plugin, ArenaManager arenaManager, ArenaEditor editor) {
|
||||
|
||||
this.plugin = plugin;
|
||||
this.arenaManager = arenaManager;
|
||||
this.editor = editor;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String[] args)
|
||||
{
|
||||
|
||||
if (args.length == 0) {
|
||||
|
||||
this.help(sender);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
String subcommand = args[0].toLowerCase(Locale.ROOT);
|
||||
switch (subcommand) {
|
||||
|
||||
case "arenas" -> this.showAvailable(sender);
|
||||
case "join" -> this.join(sender, args);
|
||||
case "leave" -> this.leave(sender);
|
||||
case "spectate", "spec" -> this.spectate(sender, args);
|
||||
case "stats" -> this.stats(sender, args);
|
||||
case "create" -> this.create(sender, args);
|
||||
case "delete" -> this.delete(sender, args);
|
||||
case "setwait" -> this.setLocation(sender, args, LocationType.WAITING);
|
||||
case "setspectator" -> this.setLocation(sender, args, LocationType.SPECTATOR);
|
||||
case "addspawn" -> this.setLocation(sender, args, LocationType.PLAYER);
|
||||
case "clearspawns" -> this.clearSpawns(sender, args);
|
||||
case "mode" -> this.mode(sender, args);
|
||||
case "enable", "disable" -> this.enabled(sender, args, subcommand.equals("enable"));
|
||||
case "layer" -> this.layer(sender, args);
|
||||
case "deathregion" -> this.deathRegion(sender, args);
|
||||
case "info" -> this.info(sender, args);
|
||||
case "cancel" -> this.cancel(sender);
|
||||
case "reload" -> this.reload(sender);
|
||||
default -> this.help(sender);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private void join(CommandSender sender, String[] args) {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player == null || !this.require(sender, "spleef.play")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
|
||||
this.showAvailable(sender);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.get(args[1]);
|
||||
if (session == null) {
|
||||
|
||||
Messages.send(sender, "&cArena not found. Use &b/spleef arenas &cto see available arenas.");
|
||||
|
||||
} else if (!session.join(player)) {
|
||||
|
||||
Messages.send(sender, "&cThat arena is unavailable, full, or already in progress.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void leave(CommandSender sender) {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.session(player);
|
||||
if (session == null) {
|
||||
|
||||
Messages.send(sender, "&cYou are not in Spleef.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
session.leave(player);
|
||||
Messages.send(sender, "&7You left Spleef.");
|
||||
|
||||
}
|
||||
|
||||
private void spectate(CommandSender sender, String[] args) {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player == null || !this.require(sender, "spleef.spectate")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
|
||||
Messages.send(sender, "&7Usage: &b/spleef spectate <arena>");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.get(args[1]);
|
||||
if (session == null || !session.spectate(player)) {
|
||||
|
||||
Messages.send(sender, "&cThat arena cannot be spectated right now.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void showAvailable(CommandSender sender) {
|
||||
|
||||
List<ArenaSession> available = this.arenaManager.availableSessions();
|
||||
if (available.isEmpty()) {
|
||||
|
||||
Messages.send(sender, "&7No arenas are currently available.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Messages.send(sender, "&7Available arenas:");
|
||||
for (ArenaSession session : available) {
|
||||
|
||||
Messages.send(sender,
|
||||
"&b" + session.arena().name() + " &8- &7" + session.arena().gameType().name() + " &8- &7"
|
||||
+ session.playerCount() + "/" + session.arena().capacity() + " &8- &f/spleef join "
|
||||
+ session.arena().name());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void stats(CommandSender sender, String[] args) {
|
||||
|
||||
if (!this.require(sender, "spleef.stats")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
PlayerStats stats;
|
||||
if (args.length < 2) {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
stats = this.arenaManager.stats().get(player.getUniqueId(), player.getName());
|
||||
|
||||
} else {
|
||||
|
||||
stats = this.arenaManager.stats().findByName(args[1]);
|
||||
if (stats == null) {
|
||||
|
||||
Messages.send(sender, "&cNo Spleef statistics were found for that player.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Messages.send(sender, "&b" + stats.name() + "&7: &a" + stats.wins() + " wins&7, &c" + stats.losses()
|
||||
+ " losses&7, &e" + stats.ties() + " ties&7, &f" + stats.games() + " games");
|
||||
|
||||
}
|
||||
|
||||
private void create(CommandSender sender, String[] args) {
|
||||
|
||||
Player player = this.adminPlayer(sender);
|
||||
if (player == null || args.length < 2) {
|
||||
|
||||
Messages.send(sender, "&7Usage: &b/spleef create <name> [classic|bow]");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
GameType gameType;
|
||||
try {
|
||||
|
||||
gameType = GameType.parse(args.length >= 3 ? args[2] : "classic");
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
|
||||
Messages.send(sender, "&cMode must be &bclassic &cor &bbow&c.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaManager.CreateResult result = this.arenaManager.create(player, args[1], gameType);
|
||||
Messages.send(sender, (result.success() ? "&a" : "&c") + result.message());
|
||||
|
||||
}
|
||||
|
||||
private void delete(CommandSender sender, String[] args) {
|
||||
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (arena == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (this.arenaManager.delete(arena)) {
|
||||
|
||||
Messages.send(sender, "&aDeleted arena &b" + arena.name() + "&a.");
|
||||
|
||||
} else {
|
||||
|
||||
Messages.send(sender, "&cAn active arena cannot be deleted.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setLocation(CommandSender sender, String[] args, LocationType type) {
|
||||
|
||||
Player player = this.adminPlayer(sender);
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (player == null || arena == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!this.arenaManager.isSetupLocationValid(arena, player.getLocation())) {
|
||||
|
||||
Messages.send(sender, "&cStand inside arena region &b" + arena.regionId() + "&c.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case WAITING -> arena.waitingSpawn(player.getLocation());
|
||||
case SPECTATOR -> arena.spectatorSpawn(player.getLocation());
|
||||
case PLAYER -> arena.addSpawn(player.getLocation());
|
||||
|
||||
}
|
||||
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aSaved the " + type.description + " for &b" + arena.name() + "&a.");
|
||||
|
||||
}
|
||||
|
||||
private void clearSpawns(CommandSender sender, String[] args) {
|
||||
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (arena == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
arena.clearSpawns();
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aCleared player spawns for &b" + arena.name() + "&a.");
|
||||
|
||||
}
|
||||
|
||||
private void mode(CommandSender sender, String[] args) {
|
||||
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (arena == null || args.length < 3) {
|
||||
|
||||
Messages.send(sender, "&7Usage: &b/spleef mode <arena> <classic|bow>");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
arena.gameType(GameType.parse(args[2]));
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aMode set to &b" + arena.gameType().name() + "&a.");
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
|
||||
Messages.send(sender, "&cMode must be &bclassic &cor &bbow&c.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void enabled(CommandSender sender, String[] args, boolean enabled) {
|
||||
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (arena == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.get(arena.name());
|
||||
if (!enabled && session.isActive()) {
|
||||
|
||||
Messages.send(sender, "&cWait for the active match to finish before disabling this arena.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
arena.enabled(enabled);
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aArena &b" + arena.name() + " &ais now " + (enabled ? "enabled" : "disabled") + ".");
|
||||
|
||||
}
|
||||
|
||||
private void layer(CommandSender sender, String[] args) {
|
||||
|
||||
if (!this.require(sender, "spleef.admin") || args.length < 3) {
|
||||
|
||||
Messages.send(sender, "&7Usage: &b/spleef layer <add|remove|list|clear> <arena> [index]");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefArena arena = this.arenaManager.get(args[2]) == null ? null : this.arenaManager.get(args[2]).arena();
|
||||
if (arena == null) {
|
||||
|
||||
Messages.send(sender, "&cArena not found.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
switch (args[1].toLowerCase(Locale.ROOT)) {
|
||||
|
||||
case "add" -> {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player != null) {
|
||||
|
||||
this.editor.beginLayer(player, arena);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
case "remove" -> {
|
||||
|
||||
if (args.length < 4) {
|
||||
|
||||
Messages.send(sender, "&7Usage: &b/spleef layer remove <arena> <index>");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
int index = Integer.parseInt(args[3]) - 1;
|
||||
if (index < 0 || index >= arena.layers().size()) {
|
||||
|
||||
throw new NumberFormatException();
|
||||
|
||||
}
|
||||
|
||||
arena.removeLayer(index);
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aLayer removed.");
|
||||
|
||||
} catch (NumberFormatException ex) {
|
||||
|
||||
Messages.send(sender, "&cInvalid layer index.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
case "list" -> {
|
||||
|
||||
if (arena.layers().isEmpty()) {
|
||||
|
||||
Messages.send(sender, "&7This arena has no layers.");
|
||||
|
||||
}
|
||||
|
||||
int index = 1;
|
||||
for (SpleefLayer layer : arena.layers()) {
|
||||
|
||||
Messages.send(sender, "&b#" + index++ + " &7" + layer.blockData().getAsString() + " &8- &7"
|
||||
+ layer.bounds().minX() + "," + layer.bounds().minY() + "," + layer.bounds().minZ() + " to "
|
||||
+ layer.bounds().maxX() + "," + layer.bounds().maxY() + "," + layer.bounds().maxZ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
case "clear" -> {
|
||||
|
||||
arena.clearLayers();
|
||||
this.arenaManager.saveArenas();
|
||||
Messages.send(sender, "&aAll layers cleared.");
|
||||
|
||||
}
|
||||
default -> Messages.send(sender, "&7Usage: &b/spleef layer <add|remove|list|clear> <arena> [index]");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void deathRegion(CommandSender sender, String[] args) {
|
||||
|
||||
Player player = this.adminPlayer(sender);
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (player != null && arena != null) {
|
||||
|
||||
this.editor.beginDeathRegion(player, arena);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void info(CommandSender sender, String[] args) {
|
||||
|
||||
SpleefArena arena = this.adminArena(sender, args, 1);
|
||||
if (arena == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.get(arena.name());
|
||||
Messages.send(sender, "&b" + arena.name() + " &8- &7region &f" + arena.regionId() + "&7, world &f"
|
||||
+ arena.worldName() + "&7, mode &f" + arena.gameType() + "&7, state &f" + session.state());
|
||||
Messages.send(sender,
|
||||
"&7Spawns: &f" + arena.spawns().size() + "&7, layers: &f" + arena.layers().size() + "&7, complete: "
|
||||
+ (arena.isComplete() ? "&aYes" : "&cNo") + "&7, enabled: "
|
||||
+ (arena.enabled() ? "&aYes" : "&cNo"));
|
||||
|
||||
}
|
||||
|
||||
private void cancel(CommandSender sender) {
|
||||
|
||||
Player player = this.player(sender);
|
||||
if (player != null) {
|
||||
|
||||
Messages.send(sender,
|
||||
this.editor.cancel(player) ? "&7Arena edit cancelled." : "&7You are not editing an arena.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void reload(CommandSender sender) {
|
||||
|
||||
if (!this.require(sender, "spleef.admin")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.plugin.reloadConfig();
|
||||
Messages.send(sender, "&aConfiguration reloaded.");
|
||||
|
||||
}
|
||||
|
||||
private SpleefArena adminArena(CommandSender sender, String[] args, int index) {
|
||||
|
||||
if (!this.require(sender, "spleef.admin")) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if (args.length <= index) {
|
||||
|
||||
Messages.send(sender, "&cAn arena name is required.");
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ArenaSession session = this.arenaManager.get(args[index]);
|
||||
if (session == null) {
|
||||
|
||||
Messages.send(sender, "&cArena not found.");
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return session.arena();
|
||||
|
||||
}
|
||||
|
||||
private Player adminPlayer(CommandSender sender) {
|
||||
|
||||
return this.require(sender, "spleef.admin") ? this.player(sender) : null;
|
||||
|
||||
}
|
||||
|
||||
private Player player(CommandSender sender) {
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
|
||||
return player;
|
||||
|
||||
}
|
||||
|
||||
Messages.send(sender, "&cThat command can only be used by a player.");
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private boolean require(CommandSender sender, String permission) {
|
||||
|
||||
if (sender.hasPermission(permission)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
Messages.send(sender, "&cYou do not have permission to do that.");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
private void help(CommandSender sender) {
|
||||
|
||||
Messages.send(sender, "&b/spleef arenas &8- &7show available arenas");
|
||||
Messages.send(sender, "&b/spleef join <arena> &8- &7join an arena");
|
||||
Messages.send(sender, "&b/spleef leave &8- &7leave Spleef");
|
||||
Messages.send(sender, "&b/spleef spectate <arena> &8- &7spectate an arena");
|
||||
Messages.send(sender, "&b/spleef stats [player] &8- &7show Spleef statistics");
|
||||
if (sender.hasPermission("spleef.admin")) {
|
||||
|
||||
Messages.send(sender,
|
||||
"&b/spleef create <name> [classic|bow] &8- &7create an arena in your WorldGuard region");
|
||||
Messages.send(sender, "&b/spleef info <arena> &8- &7show setup status");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||
@NotNull String[] args)
|
||||
{
|
||||
|
||||
if (args.length == 1) {
|
||||
|
||||
List<String> commands = new ArrayList<>(List.of("arenas", "join", "leave", "spectate", "stats"));
|
||||
if (sender.hasPermission("spleef.admin")) {
|
||||
|
||||
commands.addAll(List.of("create", "delete", "setwait", "setspectator", "addspawn", "clearspawns",
|
||||
"mode", "enable", "disable", "layer", "deathregion", "info", "cancel", "reload"));
|
||||
|
||||
}
|
||||
|
||||
return matches(args[0], commands);
|
||||
|
||||
}
|
||||
|
||||
if (args.length == 2
|
||||
&& List.of("join", "spectate", "delete", "setwait", "setspectator", "addspawn", "clearspawns", "mode",
|
||||
"enable", "disable", "deathregion", "info").contains(args[0].toLowerCase(Locale.ROOT)))
|
||||
{
|
||||
|
||||
return matches(args[1], this.arenaManager.sessions().stream().map(value -> value.arena().name()).toList());
|
||||
|
||||
}
|
||||
|
||||
if (args.length == 2 && args[0].equalsIgnoreCase("layer")) {
|
||||
|
||||
return matches(args[1], List.of("add", "remove", "list", "clear"));
|
||||
|
||||
}
|
||||
|
||||
if (args.length == 3 && args[0].equalsIgnoreCase("layer")) {
|
||||
|
||||
return matches(args[2], this.arenaManager.sessions().stream().map(value -> value.arena().name()).toList());
|
||||
|
||||
}
|
||||
|
||||
if (args.length == 3 && (args[0].equalsIgnoreCase("create") || args[0].equalsIgnoreCase("mode"))) {
|
||||
|
||||
return matches(args[2], List.of("classic", "bow"));
|
||||
|
||||
}
|
||||
|
||||
return List.of();
|
||||
|
||||
}
|
||||
|
||||
private static List<String> matches(String input, List<String> values) {
|
||||
|
||||
String prefix = input.toLowerCase(Locale.ROOT);
|
||||
return values.stream().filter(value -> value.toLowerCase(Locale.ROOT).startsWith(prefix)).toList();
|
||||
|
||||
}
|
||||
|
||||
private enum LocationType {
|
||||
|
||||
WAITING("waiting spawn"), SPECTATOR("spectator spawn"), PLAYER("player spawn");
|
||||
|
||||
private final String description;
|
||||
|
||||
LocationType(String description) {
|
||||
|
||||
this.description = description;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package net.trueog.spleefog.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.model.BlockBounds;
|
||||
import net.trueog.spleefog.model.GameType;
|
||||
import net.trueog.spleefog.model.SpleefArena;
|
||||
import net.trueog.spleefog.model.SpleefLayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
public final class ArenaRepository {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final File file;
|
||||
|
||||
public ArenaRepository(SpleefPlugin plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
this.file = new File(plugin.getDataFolder(), "arenas.yml");
|
||||
|
||||
}
|
||||
|
||||
public List<SpleefArena> load() {
|
||||
|
||||
List<SpleefArena> arenas = new ArrayList<>();
|
||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(this.file);
|
||||
ConfigurationSection root = yaml.getConfigurationSection("arenas");
|
||||
if (root == null) {
|
||||
|
||||
return arenas;
|
||||
|
||||
}
|
||||
|
||||
for (String key : root.getKeys(false)) {
|
||||
|
||||
ConfigurationSection section = root.getConfigurationSection(key);
|
||||
try {
|
||||
|
||||
SpleefArena arena = this.readArena(key, section);
|
||||
if (arena != null) {
|
||||
|
||||
arenas.add(arena);
|
||||
|
||||
}
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
this.plugin.getLogger().warning("Could not load Spleef arena '" + key + "': " + ex.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return arenas;
|
||||
|
||||
}
|
||||
|
||||
public void save(Collection<SpleefArena> arenas) {
|
||||
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
for (SpleefArena arena : arenas) {
|
||||
|
||||
String path = "arenas." + arena.name();
|
||||
yaml.set(path + ".world", arena.worldName());
|
||||
yaml.set(path + ".region", arena.regionId());
|
||||
yaml.set(path + ".region-bounds", writeBounds(arena.regionBounds()));
|
||||
yaml.set(path + ".game", arena.gameType().name());
|
||||
yaml.set(path + ".enabled", arena.enabled());
|
||||
yaml.set(path + ".waiting-spawn", arena.waitingSpawn());
|
||||
yaml.set(path + ".spectator-spawn", arena.spectatorSpawn());
|
||||
yaml.set(path + ".spawns", arena.spawns());
|
||||
yaml.set(path + ".death-region", writeBounds(arena.deathRegion()));
|
||||
|
||||
int index = 0;
|
||||
for (SpleefLayer layer : arena.layers()) {
|
||||
|
||||
String layerPath = path + ".layers." + index++;
|
||||
yaml.set(layerPath + ".bounds", writeBounds(layer.bounds()));
|
||||
yaml.set(layerPath + ".block-data", layer.blockData().getAsString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
yaml.save(this.file);
|
||||
|
||||
} catch (IOException ex) {
|
||||
|
||||
this.plugin.getLogger().severe("Could not save arenas.yml: " + ex.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private SpleefArena readArena(String name, ConfigurationSection section) {
|
||||
|
||||
if (section == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
String worldName = section.getString("world");
|
||||
String region = section.getString("region");
|
||||
BlockBounds regionBounds = readBounds(section.getConfigurationSection("region-bounds"));
|
||||
if (worldName == null || region == null || regionBounds == null || Bukkit.getWorld(worldName) == null) {
|
||||
|
||||
throw new IllegalArgumentException("world, region, or region bounds are unavailable");
|
||||
|
||||
}
|
||||
|
||||
SpleefArena arena = new SpleefArena(name, worldName, region, regionBounds,
|
||||
GameType.parse(section.getString("game", "CLASSIC")));
|
||||
arena.enabled(section.getBoolean("enabled", true));
|
||||
arena.waitingSpawn(section.getLocation("waiting-spawn"));
|
||||
arena.spectatorSpawn(section.getLocation("spectator-spawn"));
|
||||
for (Object value : section.getList("spawns", List.of())) {
|
||||
|
||||
if (value instanceof Location location) {
|
||||
|
||||
arena.addSpawn(location);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
arena.deathRegion(readBounds(section.getConfigurationSection("death-region")));
|
||||
|
||||
ConfigurationSection layers = section.getConfigurationSection("layers");
|
||||
if (layers != null) {
|
||||
|
||||
for (String key : layers.getKeys(false)) {
|
||||
|
||||
ConfigurationSection layer = layers.getConfigurationSection(key);
|
||||
BlockBounds bounds = readBounds(layer == null ? null : layer.getConfigurationSection("bounds"));
|
||||
String blockData = layer == null ? null : layer.getString("block-data");
|
||||
if (bounds != null && blockData != null) {
|
||||
|
||||
arena.addLayer(new SpleefLayer(bounds, Bukkit.createBlockData(blockData)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return arena;
|
||||
|
||||
}
|
||||
|
||||
private static Map<String, Object> writeBounds(BlockBounds bounds) {
|
||||
|
||||
if (bounds == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return Map.of("min-x", bounds.minX(), "min-y", bounds.minY(), "min-z", bounds.minZ(), "max-x", bounds.maxX(),
|
||||
"max-y", bounds.maxY(), "max-z", bounds.maxZ());
|
||||
|
||||
}
|
||||
|
||||
private static BlockBounds readBounds(ConfigurationSection section) {
|
||||
|
||||
if (section == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return new BlockBounds(section.getInt("min-x"), section.getInt("min-y"), section.getInt("min-z"),
|
||||
section.getInt("max-x"), section.getInt("max-y"), section.getInt("max-z"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package net.trueog.spleefog.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.player.PlayerSnapshot;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
public final class RecoveryStore {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final File file;
|
||||
private final Map<UUID, PlayerSnapshot> snapshots = new HashMap<>();
|
||||
|
||||
public RecoveryStore(SpleefPlugin plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
this.file = new File(plugin.getDataFolder(), "recovery.yml");
|
||||
this.load();
|
||||
|
||||
}
|
||||
|
||||
public PlayerSnapshot capture(org.bukkit.entity.Player player) {
|
||||
|
||||
PlayerSnapshot snapshot = PlayerSnapshot.capture(player);
|
||||
this.snapshots.put(player.getUniqueId(), snapshot);
|
||||
this.save();
|
||||
return snapshot;
|
||||
|
||||
}
|
||||
|
||||
public PlayerSnapshot get(UUID playerId) {
|
||||
|
||||
return this.snapshots.get(playerId);
|
||||
|
||||
}
|
||||
|
||||
public PlayerSnapshot remove(UUID playerId) {
|
||||
|
||||
PlayerSnapshot value = this.snapshots.remove(playerId);
|
||||
this.save();
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
public boolean contains(UUID playerId) {
|
||||
|
||||
return this.snapshots.containsKey(playerId);
|
||||
|
||||
}
|
||||
|
||||
private void load() {
|
||||
|
||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(this.file);
|
||||
ConfigurationSection players = yaml.getConfigurationSection("players");
|
||||
if (players == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (String key : players.getKeys(false)) {
|
||||
|
||||
try {
|
||||
|
||||
UUID playerId = UUID.fromString(key);
|
||||
this.snapshots.put(playerId, PlayerSnapshot.read(yaml, "players." + key));
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
this.plugin.getLogger().warning("Ignoring invalid recovery entry '" + key + "'.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void save() {
|
||||
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
for (Map.Entry<UUID, PlayerSnapshot> entry : this.snapshots.entrySet()) {
|
||||
|
||||
entry.getValue().write(yaml, "players." + entry.getKey());
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
yaml.save(this.file);
|
||||
|
||||
} catch (IOException ex) {
|
||||
|
||||
this.plugin.getLogger().severe("Could not save recovery.yml: " + ex.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package net.trueog.spleefog.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
public final class StatsRepository {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final File file;
|
||||
private final Map<UUID, PlayerStats> stats = new HashMap<>();
|
||||
|
||||
public StatsRepository(SpleefPlugin plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
this.file = new File(plugin.getDataFolder(), "stats.yml");
|
||||
this.load();
|
||||
|
||||
}
|
||||
|
||||
public PlayerStats get(UUID playerId, String name) {
|
||||
|
||||
PlayerStats value = this.stats.computeIfAbsent(playerId, ignored -> new PlayerStats(name));
|
||||
if (name != null && !name.isBlank()) {
|
||||
|
||||
value.name = name;
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
public PlayerStats findByName(String name) {
|
||||
|
||||
return this.stats.values().stream().filter(value -> value.name.equalsIgnoreCase(name)).findFirst().orElse(null);
|
||||
|
||||
}
|
||||
|
||||
public void recordWin(UUID playerId, String name) {
|
||||
|
||||
PlayerStats value = this.get(playerId, name);
|
||||
value.wins++;
|
||||
value.games++;
|
||||
|
||||
}
|
||||
|
||||
public void recordLoss(UUID playerId, String name) {
|
||||
|
||||
PlayerStats value = this.get(playerId, name);
|
||||
value.losses++;
|
||||
value.games++;
|
||||
|
||||
}
|
||||
|
||||
public void recordTie(UUID playerId, String name) {
|
||||
|
||||
PlayerStats value = this.get(playerId, name);
|
||||
value.ties++;
|
||||
value.games++;
|
||||
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
for (Map.Entry<UUID, PlayerStats> entry : this.stats.entrySet()) {
|
||||
|
||||
String path = "players." + entry.getKey();
|
||||
PlayerStats value = entry.getValue();
|
||||
yaml.set(path + ".name", value.name);
|
||||
yaml.set(path + ".wins", value.wins);
|
||||
yaml.set(path + ".losses", value.losses);
|
||||
yaml.set(path + ".ties", value.ties);
|
||||
yaml.set(path + ".games", value.games);
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
yaml.save(this.file);
|
||||
|
||||
} catch (IOException ex) {
|
||||
|
||||
this.plugin.getLogger().severe("Could not save stats.yml: " + ex.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void load() {
|
||||
|
||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(this.file);
|
||||
ConfigurationSection players = yaml.getConfigurationSection("players");
|
||||
if (players == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (String key : players.getKeys(false)) {
|
||||
|
||||
try {
|
||||
|
||||
UUID playerId = UUID.fromString(key);
|
||||
String path = "players." + key;
|
||||
PlayerStats value = new PlayerStats(yaml.getString(path + ".name", "Unknown"));
|
||||
value.wins = yaml.getInt(path + ".wins");
|
||||
value.losses = yaml.getInt(path + ".losses");
|
||||
value.ties = yaml.getInt(path + ".ties");
|
||||
value.games = yaml.getInt(path + ".games");
|
||||
this.stats.put(playerId, value);
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
this.plugin.getLogger().warning("Ignoring invalid player UUID in stats.yml: " + key);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class PlayerStats {
|
||||
|
||||
private String name;
|
||||
private int wins;
|
||||
private int losses;
|
||||
private int ties;
|
||||
private int games;
|
||||
|
||||
private PlayerStats(String name) {
|
||||
|
||||
this.name = name == null ? "Unknown" : name;
|
||||
|
||||
}
|
||||
|
||||
public String name() {
|
||||
|
||||
return this.name;
|
||||
|
||||
}
|
||||
|
||||
public int wins() {
|
||||
|
||||
return this.wins;
|
||||
|
||||
}
|
||||
|
||||
public int losses() {
|
||||
|
||||
return this.losses;
|
||||
|
||||
}
|
||||
|
||||
public int ties() {
|
||||
|
||||
return this.ties;
|
||||
|
||||
}
|
||||
|
||||
public int games() {
|
||||
|
||||
return this.games;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package net.trueog.spleefog.editor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.Messages;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.arena.ArenaManager;
|
||||
import net.trueog.spleefog.arena.ArenaSession;
|
||||
import net.trueog.spleefog.model.ArenaState;
|
||||
import net.trueog.spleefog.model.BlockBounds;
|
||||
import net.trueog.spleefog.model.SpleefArena;
|
||||
import net.trueog.spleefog.model.SpleefLayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public final class ArenaEditor implements Listener {
|
||||
|
||||
private final SpleefPlugin plugin;
|
||||
private final ArenaManager arenaManager;
|
||||
private final Map<UUID, EditSession> edits = new HashMap<>();
|
||||
|
||||
public ArenaEditor(SpleefPlugin plugin, ArenaManager arenaManager) {
|
||||
|
||||
this.plugin = plugin;
|
||||
this.arenaManager = arenaManager;
|
||||
|
||||
}
|
||||
|
||||
public boolean beginLayer(Player player, SpleefArena arena) {
|
||||
|
||||
if (!this.canEdit(player, arena)) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.edits.put(player.getUniqueId(), new EditSession(arena, EditType.LAYER));
|
||||
Messages.send(player, "&7Click the first corner block of the layer, or type &bcancel&7.");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public boolean beginDeathRegion(Player player, SpleefArena arena) {
|
||||
|
||||
if (!this.canEdit(player, arena)) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.edits.put(player.getUniqueId(), new EditSession(arena, EditType.DEATH_REGION));
|
||||
Messages.send(player, "&7Click the first corner block of the death region, or type &bcancel&7.");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public boolean cancel(Player player) {
|
||||
|
||||
return this.edits.remove(player.getUniqueId()) != null;
|
||||
|
||||
}
|
||||
|
||||
private boolean canEdit(Player player, SpleefArena arena) {
|
||||
|
||||
ArenaSession session = this.arenaManager.get(arena.name());
|
||||
if (session == null || session.state() != ArenaState.WAITING || session.isActive()) {
|
||||
|
||||
Messages.send(player, "&cThat arena cannot be edited while it is active.");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
|
||||
EditSession edit = this.edits.get(event.getPlayer().getUniqueId());
|
||||
if (edit == null || edit.awaitingMaterial || event.getClickedBlock() == null
|
||||
|| event.getAction() != Action.LEFT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_BLOCK)
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
Location location = event.getClickedBlock().getLocation();
|
||||
if (!this.arenaManager.isSetupLocationValid(edit.arena, location)) {
|
||||
|
||||
Messages.send(event.getPlayer(),
|
||||
"&cBoth corners must be inside WorldGuard region &b" + edit.arena.regionId() + "&c.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (edit.first == null) {
|
||||
|
||||
edit.first = location;
|
||||
Messages.send(event.getPlayer(), "&7First corner set. Click the second corner block.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
edit.second = location;
|
||||
if (edit.type == EditType.DEATH_REGION) {
|
||||
|
||||
edit.arena.deathRegion(new BlockBounds(edit.first, edit.second));
|
||||
this.arenaManager.saveArenas();
|
||||
this.edits.remove(event.getPlayer().getUniqueId());
|
||||
Messages.send(event.getPlayer(), "&aDeath region saved.");
|
||||
|
||||
} else {
|
||||
|
||||
edit.awaitingMaterial = true;
|
||||
Messages.send(event.getPlayer(),
|
||||
"&7Enter the Bukkit block data for this layer, for example &bminecraft:snow_block&7.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
|
||||
EditSession edit = this.edits.get(event.getPlayer().getUniqueId());
|
||||
if (edit == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
String input = event.getMessage().trim();
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> this.handleChat(event.getPlayer(), edit, input));
|
||||
|
||||
}
|
||||
|
||||
private void handleChat(Player player, EditSession edit, String input) {
|
||||
|
||||
if (input.equalsIgnoreCase("cancel")) {
|
||||
|
||||
this.edits.remove(player.getUniqueId());
|
||||
Messages.send(player, "&7Arena edit cancelled.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!edit.awaitingMaterial) {
|
||||
|
||||
Messages.send(player, "&7Click the requested block, or type &bcancel&7.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
BlockData blockData;
|
||||
try {
|
||||
|
||||
blockData = Bukkit.createBlockData(input);
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
|
||||
Messages.send(player, "&cInvalid block data. Try again or type &bcancel&c.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
edit.arena.addLayer(new SpleefLayer(new BlockBounds(edit.first, edit.second), blockData));
|
||||
this.arenaManager.saveArenas();
|
||||
this.edits.remove(player.getUniqueId());
|
||||
Messages.send(player, "&aLayer added to &b" + edit.arena.name() + "&a.");
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
|
||||
this.edits.remove(event.getPlayer().getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
private enum EditType {
|
||||
LAYER, DEATH_REGION
|
||||
}
|
||||
|
||||
private static final class EditSession {
|
||||
|
||||
private final SpleefArena arena;
|
||||
private final EditType type;
|
||||
private Location first;
|
||||
private Location second;
|
||||
private boolean awaitingMaterial;
|
||||
|
||||
private EditSession(SpleefArena arena, EditType type) {
|
||||
|
||||
this.arena = arena;
|
||||
this.type = type;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.trueog.spleefog.hook;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/** Keeps combat-tagged SMP players from escaping into a Spleef match. */
|
||||
public final class BattleTrackerHook {
|
||||
|
||||
private final SpleefPlugin owner;
|
||||
private Plugin cachedPlugin;
|
||||
private Method getCombatLog;
|
||||
private Method isInCombat;
|
||||
private boolean warned;
|
||||
|
||||
public BattleTrackerHook(SpleefPlugin owner) {
|
||||
|
||||
this.owner = owner;
|
||||
|
||||
}
|
||||
|
||||
public boolean isInCombat(Player player) {
|
||||
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("BattleTracker");
|
||||
if (plugin == null || !plugin.isEnabled()) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
this.resolve(plugin);
|
||||
Object combatLog = this.getCombatLog.invoke(plugin);
|
||||
return combatLog != null && Boolean.TRUE.equals(this.isInCombat.invoke(combatLog, player));
|
||||
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
|
||||
if (!this.warned) {
|
||||
|
||||
this.warned = true;
|
||||
this.owner.getLogger()
|
||||
.warning("Could not query BattleTracker combat state; Spleef join protection is disabled.");
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void resolve(Plugin plugin) throws ReflectiveOperationException {
|
||||
|
||||
if (plugin == this.cachedPlugin && this.getCombatLog != null && this.isInCombat != null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.getCombatLog = plugin.getClass().getMethod("getCombatLog");
|
||||
Object combatLog = this.getCombatLog.invoke(plugin);
|
||||
if (combatLog == null) {
|
||||
|
||||
throw new NoSuchMethodException("BattleTracker combat log is unavailable");
|
||||
|
||||
}
|
||||
|
||||
this.isInCombat = combatLog.getClass().getMethod("isInCombat", Player.class);
|
||||
this.cachedPlugin = plugin;
|
||||
this.warned = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package net.trueog.spleefog.hook;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class GameModeInventoriesHook {
|
||||
|
||||
private static final String PLUGIN_NAME = "GameModeInventories-OG";
|
||||
private static final String USE_PERMISSION = "gamemodeinventories.use";
|
||||
private final SpleefPlugin owner;
|
||||
private final boolean enabled;
|
||||
private final Map<UUID, PermissionAttachment> attachments = new HashMap<>();
|
||||
|
||||
public GameModeInventoriesHook(SpleefPlugin owner) {
|
||||
|
||||
this.owner = owner;
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME);
|
||||
this.enabled = plugin != null && plugin.isEnabled();
|
||||
|
||||
}
|
||||
|
||||
public void suspend(Player player) {
|
||||
|
||||
if (this.enabled) {
|
||||
|
||||
this.attachments.computeIfAbsent(player.getUniqueId(),
|
||||
ignored -> player.addAttachment(this.owner, USE_PERMISSION, false));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void release(Player player) {
|
||||
|
||||
PermissionAttachment attachment = this.attachments.remove(player.getUniqueId());
|
||||
if (attachment == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
attachment.remove();
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void releaseAll() {
|
||||
|
||||
for (PermissionAttachment attachment : this.attachments.values()) {
|
||||
|
||||
try {
|
||||
|
||||
attachment.remove();
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.attachments.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.trueog.spleefog.hook;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import net.trueog.spleefog.SpleefPlugin;
|
||||
import net.trueog.spleefog.api.SpleefAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventException;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.EventExecutor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class PlayerBountiesHook implements Listener {
|
||||
|
||||
private static final String PLUGIN_NAME = "PlayerBounties-OG";
|
||||
private static final String CLAIM_EVENT = "com.tcoded.playerbountiesplus.event.BountyClaimEvent";
|
||||
private static final String SET_EVENT = "com.tcoded.playerbountiesplus.event.BountySetEvent";
|
||||
|
||||
private PlayerBountiesHook() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean register(SpleefPlugin owner) {
|
||||
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME);
|
||||
if (plugin == null || !plugin.isEnabled()) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Class<? extends Event> claimClass = Class.forName(CLAIM_EVENT).asSubclass(Event.class);
|
||||
Class<? extends Event> setClass = Class.forName(SET_EVENT).asSubclass(Event.class);
|
||||
Method claimant = claimClass.getMethod("getClaimant");
|
||||
Method victim = claimClass.getMethod("getVictim");
|
||||
Method target = setClass.getMethod("getTarget");
|
||||
if (!Cancellable.class.isAssignableFrom(claimClass) || !Cancellable.class.isAssignableFrom(setClass)) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
PlayerBountiesHook listener = new PlayerBountiesHook();
|
||||
Bukkit.getPluginManager().registerEvent(claimClass, listener, EventPriority.NORMAL,
|
||||
executor(claimant, victim), owner, true);
|
||||
Bukkit.getPluginManager().registerEvent(setClass, listener, EventPriority.NORMAL, executor(target), owner,
|
||||
true);
|
||||
return true;
|
||||
|
||||
} catch (ReflectiveOperationException | ClassCastException ex) {
|
||||
|
||||
owner.getLogger().warning("PlayerBounties-OG API is incompatible; bounty protection is disabled.");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static EventExecutor executor(Method... accessors) {
|
||||
|
||||
return (listener, event) -> {
|
||||
|
||||
try {
|
||||
|
||||
for (Method accessor : accessors) {
|
||||
|
||||
Object value = accessor.invoke(event);
|
||||
if (value instanceof Player player && SpleefAPI.isInSpleef(player)) {
|
||||
|
||||
((Cancellable) event).setCancelled(true);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (ReflectiveOperationException | ClassCastException ex) {
|
||||
|
||||
throw new EventException(ex);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.trueog.spleefog.model;
|
||||
|
||||
public enum ArenaState {
|
||||
WAITING, COUNTDOWN, IN_GAME, ENDING
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.trueog.spleefog.model;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public record BlockBounds(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||
|
||||
public BlockBounds(Location first, Location second) {
|
||||
|
||||
this(Math.min(first.getBlockX(), second.getBlockX()), Math.min(first.getBlockY(), second.getBlockY()),
|
||||
Math.min(first.getBlockZ(), second.getBlockZ()), Math.max(first.getBlockX(), second.getBlockX()),
|
||||
Math.max(first.getBlockY(), second.getBlockY()), Math.max(first.getBlockZ(), second.getBlockZ()));
|
||||
|
||||
}
|
||||
|
||||
public boolean contains(Location location) {
|
||||
|
||||
return location != null && location.getBlockX() >= this.minX && location.getBlockX() <= this.maxX
|
||||
&& location.getBlockY() >= this.minY && location.getBlockY() <= this.maxY
|
||||
&& location.getBlockZ() >= this.minZ && location.getBlockZ() <= this.maxZ;
|
||||
|
||||
}
|
||||
|
||||
public long volume() {
|
||||
|
||||
return (long) (this.maxX - this.minX + 1) * (this.maxY - this.minY + 1) * (this.maxZ - this.minZ + 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.trueog.spleefog.model;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum GameType {
|
||||
|
||||
CLASSIC, BOW;
|
||||
|
||||
public static GameType parse(String value) {
|
||||
|
||||
if (value == null) {
|
||||
|
||||
return CLASSIC;
|
||||
|
||||
}
|
||||
|
||||
String normalized = value.toUpperCase(Locale.ROOT).replace('-', '_');
|
||||
if (normalized.equals("BOW_SPLEEF") || normalized.equals("BOWSPLEEF")) {
|
||||
|
||||
return BOW;
|
||||
|
||||
}
|
||||
|
||||
return valueOf(normalized);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package net.trueog.spleefog.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
public final class SpleefArena {
|
||||
|
||||
private final String name;
|
||||
private final String worldName;
|
||||
private final String regionId;
|
||||
private final BlockBounds regionBounds;
|
||||
private final List<Location> spawns = new ArrayList<>();
|
||||
private final List<SpleefLayer> layers = new ArrayList<>();
|
||||
private GameType gameType;
|
||||
private boolean enabled = true;
|
||||
private Location waitingSpawn;
|
||||
private Location spectatorSpawn;
|
||||
private BlockBounds deathRegion;
|
||||
|
||||
public SpleefArena(String name, String worldName, String regionId, BlockBounds regionBounds, GameType gameType) {
|
||||
|
||||
this.name = name;
|
||||
this.worldName = worldName;
|
||||
this.regionId = regionId;
|
||||
this.regionBounds = regionBounds;
|
||||
this.gameType = gameType;
|
||||
|
||||
}
|
||||
|
||||
public String name() {
|
||||
|
||||
return this.name;
|
||||
|
||||
}
|
||||
|
||||
public String worldName() {
|
||||
|
||||
return this.worldName;
|
||||
|
||||
}
|
||||
|
||||
public String regionId() {
|
||||
|
||||
return this.regionId;
|
||||
|
||||
}
|
||||
|
||||
public BlockBounds regionBounds() {
|
||||
|
||||
return this.regionBounds;
|
||||
|
||||
}
|
||||
|
||||
public GameType gameType() {
|
||||
|
||||
return this.gameType;
|
||||
|
||||
}
|
||||
|
||||
public void gameType(GameType gameType) {
|
||||
|
||||
this.gameType = gameType;
|
||||
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
|
||||
return this.enabled;
|
||||
|
||||
}
|
||||
|
||||
public void enabled(boolean enabled) {
|
||||
|
||||
this.enabled = enabled;
|
||||
|
||||
}
|
||||
|
||||
public Location waitingSpawn() {
|
||||
|
||||
return clone(this.waitingSpawn);
|
||||
|
||||
}
|
||||
|
||||
public void waitingSpawn(Location location) {
|
||||
|
||||
this.waitingSpawn = clone(location);
|
||||
|
||||
}
|
||||
|
||||
public Location spectatorSpawn() {
|
||||
|
||||
return clone(this.spectatorSpawn);
|
||||
|
||||
}
|
||||
|
||||
public void spectatorSpawn(Location location) {
|
||||
|
||||
this.spectatorSpawn = clone(location);
|
||||
|
||||
}
|
||||
|
||||
public List<Location> spawns() {
|
||||
|
||||
return this.spawns.stream().map(SpleefArena::clone).toList();
|
||||
|
||||
}
|
||||
|
||||
public List<SpleefLayer> layers() {
|
||||
|
||||
return List.copyOf(this.layers);
|
||||
|
||||
}
|
||||
|
||||
public BlockBounds deathRegion() {
|
||||
|
||||
return this.deathRegion;
|
||||
|
||||
}
|
||||
|
||||
public void deathRegion(BlockBounds bounds) {
|
||||
|
||||
this.deathRegion = bounds;
|
||||
|
||||
}
|
||||
|
||||
public void addSpawn(Location location) {
|
||||
|
||||
this.spawns.add(clone(location));
|
||||
|
||||
}
|
||||
|
||||
public void clearSpawns() {
|
||||
|
||||
this.spawns.clear();
|
||||
|
||||
}
|
||||
|
||||
public void addLayer(SpleefLayer layer) {
|
||||
|
||||
this.layers.add(layer);
|
||||
|
||||
}
|
||||
|
||||
public void removeLayer(int index) {
|
||||
|
||||
this.layers.remove(index);
|
||||
|
||||
}
|
||||
|
||||
public void clearLayers() {
|
||||
|
||||
this.layers.clear();
|
||||
|
||||
}
|
||||
|
||||
public boolean contains(Location location) {
|
||||
|
||||
World world = location == null ? null : location.getWorld();
|
||||
return world != null && world.getName().equalsIgnoreCase(this.worldName)
|
||||
&& this.regionBounds.contains(location);
|
||||
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
|
||||
return this.waitingSpawn != null && this.spectatorSpawn != null && this.spawns.size() >= 2
|
||||
&& !this.layers.isEmpty() && this.deathRegion != null;
|
||||
|
||||
}
|
||||
|
||||
public int capacity() {
|
||||
|
||||
return this.spawns.size();
|
||||
|
||||
}
|
||||
|
||||
private static Location clone(Location location) {
|
||||
|
||||
return location == null ? null : location.clone();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.trueog.spleefog.model;
|
||||
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public record SpleefLayer(BlockBounds bounds, BlockData blockData) {
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package net.trueog.spleefog.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
|
||||
public final class PlayerSnapshot {
|
||||
|
||||
private final Location location;
|
||||
private final ItemStack[] storage;
|
||||
private final ItemStack[] armor;
|
||||
private final ItemStack[] extra;
|
||||
private final List<PotionEffect> potionEffects;
|
||||
private final GameMode gameMode;
|
||||
private final float experience;
|
||||
private final int level;
|
||||
private final int totalExperience;
|
||||
private final double health;
|
||||
private final int foodLevel;
|
||||
private final float saturation;
|
||||
private final float exhaustion;
|
||||
private final boolean allowFlight;
|
||||
private final boolean flying;
|
||||
private final int fireTicks;
|
||||
private final float fallDistance;
|
||||
private final transient Scoreboard scoreboard;
|
||||
|
||||
private PlayerSnapshot(Location location, ItemStack[] storage, ItemStack[] armor, ItemStack[] extra,
|
||||
List<PotionEffect> potionEffects, GameMode gameMode, float experience, int level, int totalExperience,
|
||||
double health, int foodLevel, float saturation, float exhaustion, boolean allowFlight, boolean flying,
|
||||
int fireTicks, float fallDistance, Scoreboard scoreboard)
|
||||
{
|
||||
|
||||
this.location = location;
|
||||
this.storage = storage;
|
||||
this.armor = armor;
|
||||
this.extra = extra;
|
||||
this.potionEffects = potionEffects;
|
||||
this.gameMode = gameMode;
|
||||
this.experience = experience;
|
||||
this.level = level;
|
||||
this.totalExperience = totalExperience;
|
||||
this.health = health;
|
||||
this.foodLevel = foodLevel;
|
||||
this.saturation = saturation;
|
||||
this.exhaustion = exhaustion;
|
||||
this.allowFlight = allowFlight;
|
||||
this.flying = flying;
|
||||
this.fireTicks = fireTicks;
|
||||
this.fallDistance = fallDistance;
|
||||
this.scoreboard = scoreboard;
|
||||
|
||||
}
|
||||
|
||||
public static PlayerSnapshot capture(Player player) {
|
||||
|
||||
return new PlayerSnapshot(player.getLocation().clone(), clone(player.getInventory().getStorageContents()),
|
||||
clone(player.getInventory().getArmorContents()), clone(player.getInventory().getExtraContents()),
|
||||
new ArrayList<>(player.getActivePotionEffects()), player.getGameMode(), player.getExp(),
|
||||
player.getLevel(), player.getTotalExperience(), player.getHealth(), player.getFoodLevel(),
|
||||
player.getSaturation(), player.getExhaustion(), player.getAllowFlight(), player.isFlying(),
|
||||
player.getFireTicks(), player.getFallDistance(), player.getScoreboard());
|
||||
|
||||
}
|
||||
|
||||
public void restore(Player player) {
|
||||
|
||||
player.getInventory().setStorageContents(clone(this.storage));
|
||||
player.getInventory().setArmorContents(clone(this.armor));
|
||||
player.getInventory().setExtraContents(clone(this.extra));
|
||||
player.updateInventory();
|
||||
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
|
||||
player.removePotionEffect(effect.getType());
|
||||
|
||||
}
|
||||
|
||||
player.addPotionEffects(this.potionEffects);
|
||||
player.setGameMode(this.gameMode);
|
||||
player.setExp(this.experience);
|
||||
player.setLevel(this.level);
|
||||
player.setTotalExperience(this.totalExperience);
|
||||
double maximumHealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
|
||||
player.setHealth(Math.max(0.5D, Math.min(this.health, maximumHealth)));
|
||||
player.setFoodLevel(this.foodLevel);
|
||||
player.setSaturation(this.saturation);
|
||||
player.setExhaustion(this.exhaustion);
|
||||
player.setAllowFlight(this.allowFlight);
|
||||
player.setFlying(this.allowFlight && this.flying);
|
||||
player.setFireTicks(this.fireTicks);
|
||||
player.setFallDistance(this.fallDistance);
|
||||
player.setScoreboard(
|
||||
this.scoreboard == null ? Bukkit.getScoreboardManager().getMainScoreboard() : this.scoreboard);
|
||||
if (this.location != null && this.location.getWorld() != null) {
|
||||
|
||||
player.teleport(this.location);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void write(ConfigurationSection yaml, String path) {
|
||||
|
||||
yaml.set(path + ".location", this.location);
|
||||
yaml.set(path + ".storage", Arrays.asList(this.storage));
|
||||
yaml.set(path + ".armor", Arrays.asList(this.armor));
|
||||
yaml.set(path + ".extra", Arrays.asList(this.extra));
|
||||
yaml.set(path + ".game-mode", this.gameMode.name());
|
||||
yaml.set(path + ".experience", this.experience);
|
||||
yaml.set(path + ".level", this.level);
|
||||
yaml.set(path + ".total-experience", this.totalExperience);
|
||||
yaml.set(path + ".health", this.health);
|
||||
yaml.set(path + ".food-level", this.foodLevel);
|
||||
yaml.set(path + ".saturation", this.saturation);
|
||||
yaml.set(path + ".exhaustion", this.exhaustion);
|
||||
yaml.set(path + ".allow-flight", this.allowFlight);
|
||||
yaml.set(path + ".flying", this.flying);
|
||||
yaml.set(path + ".fire-ticks", this.fireTicks);
|
||||
yaml.set(path + ".fall-distance", this.fallDistance);
|
||||
|
||||
List<Map<String, Object>> effects = new ArrayList<>();
|
||||
for (PotionEffect effect : this.potionEffects) {
|
||||
|
||||
Map<String, Object> value = new LinkedHashMap<>();
|
||||
value.put("type", effect.getType().getName());
|
||||
value.put("duration", effect.getDuration());
|
||||
value.put("amplifier", effect.getAmplifier());
|
||||
value.put("ambient", effect.isAmbient());
|
||||
value.put("particles", effect.hasParticles());
|
||||
value.put("icon", effect.hasIcon());
|
||||
effects.add(value);
|
||||
|
||||
}
|
||||
|
||||
yaml.set(path + ".potion-effects", effects);
|
||||
|
||||
}
|
||||
|
||||
public static PlayerSnapshot read(ConfigurationSection yaml, String path) {
|
||||
|
||||
Location location = yaml.getLocation(path + ".location");
|
||||
ItemStack[] storage = readItems(yaml.getList(path + ".storage", List.of()), 36);
|
||||
ItemStack[] armor = readItems(yaml.getList(path + ".armor", List.of()), 4);
|
||||
ItemStack[] extra = readItems(yaml.getList(path + ".extra", List.of()), 1);
|
||||
List<PotionEffect> effects = readEffects(yaml.getMapList(path + ".potion-effects"));
|
||||
GameMode gameMode;
|
||||
try {
|
||||
|
||||
gameMode = GameMode.valueOf(yaml.getString(path + ".game-mode", "SURVIVAL"));
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
gameMode = GameMode.SURVIVAL;
|
||||
|
||||
}
|
||||
|
||||
return new PlayerSnapshot(location, storage, armor, extra, effects, gameMode,
|
||||
(float) yaml.getDouble(path + ".experience"), yaml.getInt(path + ".level"),
|
||||
yaml.getInt(path + ".total-experience"), yaml.getDouble(path + ".health", 20.0D),
|
||||
yaml.getInt(path + ".food-level", 20), (float) yaml.getDouble(path + ".saturation", 5.0D),
|
||||
(float) yaml.getDouble(path + ".exhaustion"), yaml.getBoolean(path + ".allow-flight"),
|
||||
yaml.getBoolean(path + ".flying"), yaml.getInt(path + ".fire-ticks"),
|
||||
(float) yaml.getDouble(path + ".fall-distance"), null);
|
||||
|
||||
}
|
||||
|
||||
private static ItemStack[] clone(ItemStack[] contents) {
|
||||
|
||||
ItemStack[] result = new ItemStack[contents.length];
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
|
||||
result[i] = contents[i] == null ? null : contents[i].clone();
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static ItemStack[] readItems(List<?> values, int defaultSize) {
|
||||
|
||||
ItemStack[] result = new ItemStack[Math.max(defaultSize, values.size())];
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
|
||||
if (values.get(i) instanceof ItemStack item) {
|
||||
|
||||
result[i] = item;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static List<PotionEffect> readEffects(List<Map<?, ?>> values) {
|
||||
|
||||
List<PotionEffect> result = new ArrayList<>();
|
||||
for (Map<?, ?> value : values) {
|
||||
|
||||
PotionEffectType type = PotionEffectType.getByName(String.valueOf(value.get("type")));
|
||||
if (type == null) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
result.add(new PotionEffect(type, number(value.get("duration")), number(value.get("amplifier")),
|
||||
bool(value.get("ambient")), bool(value.get("particles")), bool(value.get("icon"))));
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static int number(Object value) {
|
||||
|
||||
return value instanceof Number number ? number.intValue() : 0;
|
||||
|
||||
}
|
||||
|
||||
private static boolean bool(Object value) {
|
||||
|
||||
return value instanceof Boolean bool && bool;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.competition.phase.CompetitionPhaseType;
|
||||
import org.battleplugins.arena.competition.phase.phases.CountdownPhase;
|
||||
import org.battleplugins.arena.config.ArenaConfigParser;
|
||||
import org.battleplugins.arena.config.ParseException;
|
||||
import org.battleplugins.arena.event.action.EventActionType;
|
||||
import org.battleplugins.arena.spleef.action.GiveShovelAction;
|
||||
import org.battleplugins.arena.spleef.action.PasteLayersAction;
|
||||
import org.battleplugins.arena.spleef.api.SpleefApiListener;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefArena;
|
||||
import org.battleplugins.arena.spleef.hook.GameModeInventoriesListener;
|
||||
import org.battleplugins.arena.spleef.hook.PlayerBountiesListener;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ArenaSpleef extends JavaPlugin {
|
||||
|
||||
public static EventActionType<GiveShovelAction> GIVE_SHOVEL = EventActionType.create("give-shovel",
|
||||
GiveShovelAction.class, GiveShovelAction::new);
|
||||
public static EventActionType<PasteLayersAction> PASTE_LAYERS = EventActionType.create("paste-layers",
|
||||
PasteLayersAction.class, PasteLayersAction::new);
|
||||
|
||||
public static final CompetitionPhaseType<?, ? extends CountdownPhase<?>> WAITING_FOR_PLAYERS = CompetitionPhaseType
|
||||
.create("waitingforplayers", CountdownPhase.class);
|
||||
|
||||
private static ArenaSpleef instance;
|
||||
|
||||
private SpleefConfig config;
|
||||
private GameModeInventoriesListener gameModeInventoriesListener;
|
||||
|
||||
private final NamespacedKey spleefItemKey = new NamespacedKey(this, "spleef_item");
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) {
|
||||
|
||||
SpleefFlags.register(this);
|
||||
|
||||
} else {
|
||||
|
||||
this.getSLF4JLogger().warn(
|
||||
"WorldGuard not present at load time; the 'allow-spleef' region flag will not be registered.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
instance = this;
|
||||
|
||||
this.saveDefaultConfig();
|
||||
|
||||
File configFile = new File(this.getDataFolder(), "config.yml");
|
||||
Configuration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
try {
|
||||
|
||||
this.config = ArenaConfigParser.newInstance(configFile.toPath(), SpleefConfig.class, config);
|
||||
|
||||
} catch (ParseException e) {
|
||||
|
||||
ParseException.handle(e);
|
||||
|
||||
this.getSLF4JLogger().error("Failed to load Spleef configuration! Disabling plugin.");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Path dataFolder = this.getDataFolder().toPath();
|
||||
Path arenasPath = dataFolder.resolve("arenas");
|
||||
if (Files.notExists(arenasPath)) {
|
||||
|
||||
this.saveResource("arenas/spleef.yml", false);
|
||||
|
||||
}
|
||||
|
||||
BattleArena.getInstance().registerArena(this, "Spleef", SpleefArena.class, SpleefArena::new);
|
||||
|
||||
this.getServer().getPluginManager().registerEvents(new SpleefApiListener(), this);
|
||||
|
||||
this.gameModeInventoriesListener = GameModeInventoriesListener.register(this);
|
||||
if (this.gameModeInventoriesListener != null) {
|
||||
|
||||
this.getSLF4JLogger()
|
||||
.info("Hooked into GameModeInventories-OG; Spleef will preserve gamemode-specific inventories.");
|
||||
|
||||
}
|
||||
|
||||
if (PlayerBountiesListener.register(this)) {
|
||||
|
||||
this.getSLF4JLogger().info(
|
||||
"Hooked into PlayerBounties-OG; bounty claims and placements for players in Spleef will be cancelled.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
if (this.gameModeInventoriesListener != null) {
|
||||
|
||||
this.gameModeInventoriesListener.releaseAll();
|
||||
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
|
||||
}
|
||||
|
||||
public SpleefConfig getMainConfig() {
|
||||
|
||||
return this.config;
|
||||
|
||||
}
|
||||
|
||||
public NamespacedKey getSpleefItemKey() {
|
||||
|
||||
return this.spleefItemKey;
|
||||
|
||||
}
|
||||
|
||||
public static ArenaSpleef getInstance() {
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.config.ArenaOption;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class SpleefConfig {
|
||||
|
||||
@ArenaOption(name = "shovels", description = "The shovels for this spleef game.", required = true)
|
||||
private Map<String, ItemStack> shovels;
|
||||
|
||||
@ArenaOption(name = "world-whitelist", description = "Worlds where Spleef arenas may be created and run. Empty list = all worlds allowed.")
|
||||
private List<String> worldWhitelist;
|
||||
|
||||
@ArenaOption(name = "region-whitelist", description = "WorldGuard regions where Spleef arenas may be created and matches may modify blocks. Empty list = all regions allowed.")
|
||||
private List<String> regionWhitelist;
|
||||
|
||||
@Nullable
|
||||
public ItemStack getShovel(String name) {
|
||||
|
||||
if (this.shovels == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ItemStack shovel = this.shovels.get(name);
|
||||
return shovel == null ? null : shovel.clone();
|
||||
|
||||
}
|
||||
|
||||
public boolean isWorldAllowed(String worldName) {
|
||||
|
||||
if (this.worldWhitelist == null || this.worldWhitelist.isEmpty()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (worldName == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
String target = worldName.toLowerCase(Locale.ROOT);
|
||||
for (String allowed : this.worldWhitelist) {
|
||||
|
||||
if (allowed != null && allowed.toLowerCase(Locale.ROOT).equals(target)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public List<String> getWorldWhitelist() {
|
||||
|
||||
return this.worldWhitelist == null ? List.of() : List.copyOf(this.worldWhitelist);
|
||||
|
||||
}
|
||||
|
||||
public boolean hasRegionWhitelist() {
|
||||
|
||||
return this.regionWhitelist != null && !this.regionWhitelist.isEmpty();
|
||||
|
||||
}
|
||||
|
||||
public boolean isRegionAllowed(String regionName) {
|
||||
|
||||
if (!this.hasRegionWhitelist()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (regionName == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
String target = regionName.toLowerCase(Locale.ROOT);
|
||||
for (String allowed : this.regionWhitelist) {
|
||||
|
||||
if (allowed != null && allowed.toLowerCase(Locale.ROOT).equals(target)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public boolean isRegionAllowedAt(Location location) {
|
||||
|
||||
if (!this.hasRegionWhitelist()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return WorldGuardSupport.isInsideAnyRegion(location, this.regionWhitelist);
|
||||
|
||||
}
|
||||
|
||||
public List<String> getRegionWhitelist() {
|
||||
|
||||
return this.regionWhitelist == null ? List.of() : List.copyOf(this.regionWhitelist);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.competition.Competition;
|
||||
import org.battleplugins.arena.competition.LiveCompetition;
|
||||
import org.battleplugins.arena.event.ArenaEventHandler;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefArena;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefCompetition;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefGame;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.entity.Snowball;
|
||||
import org.bukkit.event.block.TNTPrimeEvent;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Contains custom event resolvers for Spleef events.
|
||||
* <p>
|
||||
* The events below would normally not be captured by the
|
||||
* {@link ArenaEventHandler} annotation since there is no default logic for them
|
||||
* to link back to a {@link Competition}. However, these custom resolvers allow
|
||||
* for these events to be listened on, along with segmenting the logic to the
|
||||
* appropriate arena and spleef game mode.
|
||||
*/
|
||||
public final class SpleefEventResolvers {
|
||||
|
||||
public static final Function<ProjectileHitEvent, LiveCompetition<?>> PROJECTILE_HIT = event -> {
|
||||
|
||||
if (event.getHitBlock() == null) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
Projectile projectile = event.getEntity();
|
||||
ProjectileSource shooter = projectile.getShooter();
|
||||
if (!(shooter instanceof Player player)) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ArenaPlayer arenaPlayer = ArenaPlayer.getArenaPlayer(player);
|
||||
if (arenaPlayer == null || !(arenaPlayer.getArena() instanceof SpleefArena arena)) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config != null && !config.isRegionAllowedAt(event.getHitBlock().getLocation())) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if (projectile instanceof Snowball) {
|
||||
|
||||
if (arena.getGame() == SpleefGame.CLASSIC) {
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if (projectile instanceof AbstractArrow) {
|
||||
|
||||
if (arena.getGame() == SpleefGame.BOW_SPLEEF) {
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
public static final Function<SpleefArena, Function<TNTPrimeEvent, LiveCompetition<?>>> TNT_PRIME = arena -> event -> {
|
||||
|
||||
if (arena.getGame() != SpleefGame.BOW_SPLEEF) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isEnabled()) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
List<Competition<?>> competitions = BattleArena.getInstance().getCompetitions(arena);
|
||||
for (Competition<?> competition : competitions) {
|
||||
|
||||
if (!(competition instanceof SpleefCompetition spleefCompetition)) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (!spleefCompetition.getMap().getWorld().equals(event.getBlock().getWorld())) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
SpleefMap map = (SpleefMap) spleefCompetition.getMap();
|
||||
String worldGuardRegion = map.getWorldGuardRegion();
|
||||
if (worldGuardRegion == null || worldGuardRegion.isBlank()) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config != null && !config.isRegionAllowedAt(event.getBlock().getLocation())) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (WorldGuardSupport.isInsideRegion(event.getBlock().getLocation(), worldGuardRegion)) {
|
||||
|
||||
return spleefCompetition;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.command.ArenaCommand;
|
||||
import org.battleplugins.arena.command.ArenaCommandExecutor;
|
||||
import org.battleplugins.arena.command.Argument;
|
||||
import org.battleplugins.arena.competition.map.CompetitionMap;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.config.ParseException;
|
||||
import org.battleplugins.arena.messages.Messages;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefArena;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.battleplugins.arena.spleef.editor.SpleefEditorWizards;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SpleefExecutor extends ArenaCommandExecutor {
|
||||
|
||||
public SpleefExecutor(SpleefArena arena) {
|
||||
|
||||
super(arena);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Player player) {
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
String worldName = player.getWorld().getName();
|
||||
if (config != null && !config.isWorldAllowed(worldName)) {
|
||||
|
||||
SpleefMessages.CREATE_WORLD_NOT_WHITELISTED.send(player, worldName);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isEnabled()) {
|
||||
|
||||
SpleefMessages.CREATE_WG_MISSING.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (config != null && !config.isRegionAllowedAt(player.getLocation())) {
|
||||
|
||||
SpleefMessages.CREATE_REGION_NOT_WHITELISTED.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isSpleefAllowedAt(player)) {
|
||||
|
||||
SpleefMessages.CREATE_WG_FLAG_REQUIRED.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
super.create(player);
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "layer", subCommands = "add", description = "Adds a layer to a spleef arena.", permissionNode = "layer.add")
|
||||
public void layer(Player player, CompetitionMap map) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
SpleefEditorWizards.ADD_LAYER.openWizard(player, this.arena, ctx -> ctx.setMap(spleefMap));
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "layer", subCommands = "remove", description = "Removes a layer from a spleef arena.", permissionNode = "layer.remove")
|
||||
public void removeLayer(Player player, CompetitionMap map, @Argument(name = "index") int index) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
if (index < 0 || index >= spleefMap.getLayers().size()) {
|
||||
|
||||
SpleefMessages.INVALID_LAYER.send(player, Integer.toString(spleefMap.getLayers().size()));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
spleefMap.removeLayer(spleefMap.getLayers().get(index));
|
||||
SpleefMessages.LAYER_REMOVED.send(player);
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "layer", subCommands = "clear", description = "Clears all layers from a spleef arena.", permissionNode = "layer.clear")
|
||||
public void clearLayer(Player player, CompetitionMap map) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
spleefMap.clearLayers();
|
||||
SpleefMessages.LAYER_REMOVED_ALL.send(player);
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "layer", subCommands = "index", description = "Changes the index of a layer.", permissionNode = "layer.index")
|
||||
public void indexLayer(Player player, CompetitionMap map, @Argument(name = "from") int from,
|
||||
@Argument(name = "to") int to)
|
||||
{
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
from--;
|
||||
to--;
|
||||
|
||||
if (spleefMap.getLayers().isEmpty()) {
|
||||
|
||||
SpleefMessages.NO_LAYERS.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (from < 0 || from >= spleefMap.getLayers().size() || to < 0 || to >= spleefMap.getLayers().size()) {
|
||||
|
||||
SpleefMessages.INVALID_LAYER.send(player, Integer.toString(spleefMap.getLayers().size()));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefMap.Layer layer = spleefMap.getLayers().get(from);
|
||||
spleefMap.removeLayer(layer);
|
||||
spleefMap.addLayer(to, layer);
|
||||
|
||||
SpleefMessages.LAYER_INDEX_CHANGED.send(player);
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "layer", subCommands = "list", description = "Lists all layers in a spleef arena.", permissionNode = "layer.list")
|
||||
public void listLayers(Player player, CompetitionMap map) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
if (spleefMap.getLayers().isEmpty()) {
|
||||
|
||||
SpleefMessages.NO_LAYERS.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.sendHeader(player);
|
||||
for (int i = 0; i < spleefMap.getLayers().size(); i++) {
|
||||
|
||||
SpleefMap.Layer layer = spleefMap.getLayers().get(i);
|
||||
Bounds bounds = layer.getBounds();
|
||||
String minBounds = bounds.getMinX() + ", " + bounds.getMinY() + ", " + bounds.getMinZ();
|
||||
String maxBounds = bounds.getMaxX() + ", " + bounds.getMaxY() + ", " + bounds.getMaxZ();
|
||||
|
||||
SpleefMessages.LAYER_INFO.send(player, Integer.toString(i + 1), minBounds, maxBounds,
|
||||
layer.getBlockData().getAsString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "deathregion", description = "Sets the death region for a spleef arena.", permissionNode = "deathregion")
|
||||
public void setDeathRegion(Player player, CompetitionMap map) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
SpleefEditorWizards.DEATH_REGION.openWizard(player, this.arena, ctx -> ctx.setMap(spleefMap));
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "worldguardregion", description = "Sets the WorldGuard region for a spleef arena.", permissionNode = "worldguardregion")
|
||||
public void setWorldGuardRegion(Player player, CompetitionMap map, @Argument(name = "region") String region) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isEnabled()) {
|
||||
|
||||
SpleefMessages.CREATE_WG_MISSING.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (spleefMap.getWorld() == null || !WorldGuardSupport.regionExists(spleefMap.getWorld(), region)) {
|
||||
|
||||
SpleefMessages.WORLDGUARD_REGION_NOT_FOUND.send(player, region);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
spleefMap.setWorldGuardRegion(region);
|
||||
|
||||
try {
|
||||
|
||||
spleefMap.save();
|
||||
|
||||
} catch (ParseException | IOException e) {
|
||||
|
||||
BattleArena.getInstance().error("Failed to save map file for arena {}", this.arena.getName(), e);
|
||||
Messages.MAP_FAILED_TO_SAVE.send(player, spleefMap.getName());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefMessages.WORLDGUARD_REGION_SET.send(player, region);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.registry.FlagConflictException;
|
||||
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class SpleefFlags {
|
||||
|
||||
public static final String ALLOW_SPLEEF_FLAG_NAME = "allow-spleef";
|
||||
|
||||
private static StateFlag allowSpleefFlag;
|
||||
|
||||
private SpleefFlags() {
|
||||
|
||||
}
|
||||
|
||||
public static void register(Plugin plugin) {
|
||||
|
||||
FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry();
|
||||
try {
|
||||
|
||||
StateFlag flag = new StateFlag(ALLOW_SPLEEF_FLAG_NAME, false);
|
||||
registry.register(flag);
|
||||
allowSpleefFlag = flag;
|
||||
|
||||
} catch (FlagConflictException conflict) {
|
||||
|
||||
Flag<?> existing = registry.get(ALLOW_SPLEEF_FLAG_NAME);
|
||||
if (existing instanceof StateFlag stateFlag) {
|
||||
|
||||
allowSpleefFlag = stateFlag;
|
||||
plugin.getSLF4JLogger().info("Reusing existing WorldGuard flag '{}'.", ALLOW_SPLEEF_FLAG_NAME);
|
||||
|
||||
} else {
|
||||
|
||||
plugin.getSLF4JLogger().error(
|
||||
"WorldGuard flag '{}' is already registered with an incompatible type; spleef will not enforce it.",
|
||||
ALLOW_SPLEEF_FLAG_NAME);
|
||||
|
||||
}
|
||||
|
||||
} catch (IllegalStateException late) {
|
||||
|
||||
plugin.getSLF4JLogger().error(
|
||||
"Failed to register WorldGuard flag '{}' — registry already locked. Ensure Spleef-OG loads before WorldGuard enables.",
|
||||
ALLOW_SPLEEF_FLAG_NAME, late);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static StateFlag getAllowSpleefFlag() {
|
||||
|
||||
return allowSpleefFlag;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.messages.Message;
|
||||
import org.battleplugins.arena.messages.Messages;
|
||||
|
||||
public final class SpleefMessages {
|
||||
|
||||
public static final Message LAYER_SET_MIN_POSITION = Messages.info("spleef-editor-layer-set-min-position",
|
||||
"Click a block to set the minimum (first) position of the layer.");
|
||||
public static final Message LAYER_SET_MAX_POSITION = Messages.info("spleef-editor-layer-set-max-position",
|
||||
"Click a block to set the maximum (second) position of the layer.");
|
||||
public static final Message LAYER_SET_BLOCK_DATA = Messages.info("spleef-editor-set-block-data",
|
||||
"Enter the name of the block the layer should be made of. Type \"cancel\" to cancel.");
|
||||
public static final Message LAYER_SET_BLOCK_DATA_INVALID = Messages.error("spleef-set-block-data-invalid",
|
||||
"Invalid block name. Please try again.");
|
||||
public static final Message DEATH_REGION_SET_MIN_POSITION = Messages.info(
|
||||
"spleef-editor-death-region-set-min-position",
|
||||
"Click a block to set the minimum (first) position of the death region.");
|
||||
public static final Message DEATH_REGION_SET_MAX_POSITION = Messages.info(
|
||||
"spleef-editor-death-region-set-max-position",
|
||||
"Click a block to set the maximum (second) position of the death region.");
|
||||
|
||||
public static final Message LAYER_ADDED = Messages.success("spleef-layer-added", "Layer added successfully!");
|
||||
public static final Message LAYER_REMOVED = Messages.success("spleef-layer-removed", "Layer removed successfully!");
|
||||
public static final Message LAYER_REMOVED_ALL = Messages.success("spleef-layer-removed-all",
|
||||
"All layers removed successfully!");
|
||||
public static final Message LAYER_INDEX_CHANGED = Messages.success("spleef-layer-index-changed",
|
||||
"Layer index changed successfully!");
|
||||
public static final Message LAYER_INFO = Messages.info("spleef-layer-info",
|
||||
"Layer <secondary>#{}</secondary>: <secondary>Min:</secondary> <primary>{}</primary>, <secondary>Max:</secondary> <primary>{}</primary>, <secondary>Block:</secondary> <primary>{}</primary>");
|
||||
public static final Message INVALID_LAYER = Messages.error("spleef-invalid-layer",
|
||||
"Invalid layer! There are only <secondary>{}</secondary> layers.");
|
||||
public static final Message NO_LAYERS = Messages.error("spleef-no-layers", "There are no layers in this map!");
|
||||
public static final Message DEATH_REGION_SET = Messages.success("spleef-death-region-set",
|
||||
"Death region set successfully!");
|
||||
|
||||
public static final Message CREATE_WORLD_NOT_WHITELISTED = Messages.error("spleef-create-world-not-whitelisted",
|
||||
"Spleef arenas cannot be created in this world. World <secondary>{}</secondary> is not in the Spleef world whitelist.");
|
||||
public static final Message CREATE_REGION_NOT_WHITELISTED = Messages.error("spleef-create-region-not-whitelisted",
|
||||
"Spleef arenas cannot be created here. Stand inside a WorldGuard region listed in the Spleef region whitelist.");
|
||||
public static final Message CREATE_WG_FLAG_REQUIRED = Messages.error("spleef-create-wg-flag-required",
|
||||
"You must stand inside a WorldGuard region that has the <secondary>allow-spleef</secondary> flag set to <secondary>allow</secondary> to create a Spleef arena here.");
|
||||
public static final Message CREATE_WG_MISSING = Messages.error("spleef-create-wg-missing",
|
||||
"WorldGuard is not loaded; Spleef arena creation is disabled.");
|
||||
public static final Message WORLD_BLOCKED_AT_RUNTIME = Messages.error("spleef-world-blocked",
|
||||
"Spleef matches are not permitted in world <secondary>{}</secondary>.");
|
||||
public static final Message MATCH_REGION_NOT_WHITELISTED = Messages.error("spleef-match-region-not-whitelisted",
|
||||
"This Spleef map is not inside a whitelisted WorldGuard region.");
|
||||
public static final Message WORLDGUARD_REGION_NOT_FOUND = Messages.error("spleef-worldguard-region-not-found",
|
||||
"WorldGuard region <secondary>{}</secondary> was not found in this map's world.");
|
||||
public static final Message WORLDGUARD_REGION_SET = Messages.success("spleef-worldguard-region-set",
|
||||
"WorldGuard region set to <secondary>{}</secondary>.");
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class WorldGuardSupport {
|
||||
|
||||
private WorldGuardSupport() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
|
||||
return Bukkit.getPluginManager().isPluginEnabled("WorldGuard");
|
||||
|
||||
}
|
||||
|
||||
public static boolean isInsideRegion(Location location, String regionId) {
|
||||
|
||||
if (location.getWorld() == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(location.getWorld()));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
BlockVector3 position = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
ApplicableRegionSet regions = regionManager.getApplicableRegions(position);
|
||||
return regions.getRegions().stream().anyMatch(region -> region.getId().equalsIgnoreCase(regionId));
|
||||
|
||||
}
|
||||
|
||||
public static boolean isInsideAnyRegion(Location location, List<String> regionIds) {
|
||||
|
||||
if (location == null || location.getWorld() == null || regionIds == null || regionIds.isEmpty()) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(location.getWorld()));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
BlockVector3 position = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
ApplicableRegionSet regions = regionManager.getApplicableRegions(position);
|
||||
return regions.getRegions().stream().anyMatch(region -> regionIds.stream()
|
||||
.anyMatch(allowed -> allowed != null && region.getId().equalsIgnoreCase(allowed)));
|
||||
|
||||
}
|
||||
|
||||
public static boolean regionExists(World world, String regionId) {
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(world));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return regionManager.getRegion(regionId) != null;
|
||||
|
||||
}
|
||||
|
||||
public static boolean isSpleefAllowedAt(Player player) {
|
||||
|
||||
StateFlag flag = SpleefFlags.getAllowSpleefFlag();
|
||||
if (flag == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
|
||||
Location location = player.getLocation();
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(location.getWorld()));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
BlockVector3 position = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
ApplicableRegionSet regions = regionManager.getApplicableRegions(position);
|
||||
return regions.testState(localPlayer, flag);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.action;
|
||||
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.event.action.EventAction;
|
||||
import org.battleplugins.arena.resolver.Resolvable;
|
||||
import org.battleplugins.arena.spleef.ArenaSpleef;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GiveShovelAction extends EventAction {
|
||||
|
||||
private static final String SHOVEL_KEY = "shovel";
|
||||
|
||||
public GiveShovelAction(Map<String, String> params) {
|
||||
|
||||
super(params, SHOVEL_KEY);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(ArenaPlayer arenaPlayer, Resolvable resolvable) {
|
||||
|
||||
ItemStack shovel = ArenaSpleef.getInstance().getMainConfig().getShovel(this.get(SHOVEL_KEY));
|
||||
if (shovel == null) {
|
||||
|
||||
ArenaSpleef.getInstance().getSLF4JLogger()
|
||||
.warn("Invalid shovel " + this.get(SHOVEL_KEY) + ". Not giving shovel to player.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
shovel.editMeta(meta -> meta.getPersistentDataContainer().set(ArenaSpleef.getInstance().getSpleefItemKey(),
|
||||
PersistentDataType.BOOLEAN, true));
|
||||
arenaPlayer.getPlayer().getInventory().addItem(shovel);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.action;
|
||||
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.competition.Competition;
|
||||
import org.battleplugins.arena.event.action.EventAction;
|
||||
import org.battleplugins.arena.resolver.Resolvable;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefCompetition;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PasteLayersAction extends EventAction {
|
||||
|
||||
public PasteLayersAction(Map<String, String> params) {
|
||||
|
||||
super(params);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(ArenaPlayer arenaPlayer, Resolvable resolvable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(Arena arena, Competition<?> competition, Resolvable resolvable) {
|
||||
|
||||
if (!(competition instanceof SpleefCompetition spleefCompetition)) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
spleefCompetition.pasteLayers();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.api;
|
||||
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefCompetition;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class SpleefAPI {
|
||||
|
||||
private static final Set<UUID> PLAYERS_IN_SPLEEF = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private SpleefAPI() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isInSpleef(Player player) {
|
||||
|
||||
if (player == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return PLAYERS_IN_SPLEEF.contains(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
static boolean markJoined(Player player) {
|
||||
|
||||
return PLAYERS_IN_SPLEEF.add(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
static boolean markLeft(Player player) {
|
||||
|
||||
return PLAYERS_IN_SPLEEF.remove(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
static boolean isSpleefPlayer(ArenaPlayer arenaPlayer) {
|
||||
|
||||
return arenaPlayer != null && arenaPlayer.getCompetition() instanceof SpleefCompetition;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.api;
|
||||
|
||||
import org.battleplugins.arena.event.player.ArenaJoinEvent;
|
||||
import org.battleplugins.arena.event.player.ArenaLeaveEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public final class SpleefApiListener implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onArenaJoin(ArenaJoinEvent event) {
|
||||
|
||||
if (!SpleefAPI.isSpleefPlayer(event.getArenaPlayer())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
if (SpleefAPI.markJoined(player)) {
|
||||
|
||||
Bukkit.getPluginManager().callEvent(new SpleefJoinEvent(player));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onArenaLeave(ArenaLeaveEvent event) {
|
||||
|
||||
if (!SpleefAPI.isSpleefPlayer(event.getArenaPlayer())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
if (SpleefAPI.markLeft(player)) {
|
||||
|
||||
Bukkit.getPluginManager().callEvent(new SpleefLeaveEvent(player));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SpleefJoinEvent extends PlayerEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
public SpleefJoinEvent(Player player) {
|
||||
|
||||
super(player);
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
|
||||
return HANDLERS;
|
||||
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
|
||||
return HANDLERS;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
import io.papermc.paper.math.BlockPosition;
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.command.ArenaCommandExecutor;
|
||||
import org.battleplugins.arena.competition.map.MapFactory;
|
||||
import org.battleplugins.arena.competition.phase.CompetitionPhaseType;
|
||||
import org.battleplugins.arena.config.ArenaOption;
|
||||
import org.battleplugins.arena.event.ArenaEventHandler;
|
||||
import org.battleplugins.arena.event.arena.ArenaPhaseStartEvent;
|
||||
import org.battleplugins.arena.event.player.ArenaLeaveEvent;
|
||||
import org.battleplugins.arena.options.ArenaOptionType;
|
||||
import org.battleplugins.arena.options.types.BooleanArenaOption;
|
||||
import org.battleplugins.arena.spleef.ArenaSpleef;
|
||||
import org.battleplugins.arena.spleef.SpleefEventResolvers;
|
||||
import org.battleplugins.arena.spleef.SpleefExecutor;
|
||||
import org.battleplugins.arena.spleef.SpleefMessages;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Snow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.TNTPrimeEvent;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SpleefArena extends Arena {
|
||||
|
||||
@ArenaOption(name = "game", description = "The spleef game.")
|
||||
private SpleefGame game = SpleefGame.CLASSIC;
|
||||
|
||||
public SpleefArena() {
|
||||
|
||||
super();
|
||||
|
||||
this.getEventManager().registerArenaResolver(ProjectileHitEvent.class, SpleefEventResolvers.PROJECTILE_HIT);
|
||||
this.getEventManager().registerArenaResolver(TNTPrimeEvent.class, SpleefEventResolvers.TNT_PRIME.apply(this));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArenaCommandExecutor createCommandExecutor() {
|
||||
|
||||
return new SpleefExecutor(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapFactory getMapFactory() {
|
||||
|
||||
return SpleefMap.FACTORY;
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler
|
||||
public void onPhaseStart(ArenaPhaseStartEvent event) {
|
||||
|
||||
if (!CompetitionPhaseType.INGAME.equals(event.getPhase().getType())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (event.getCompetition() instanceof SpleefCompetition spleefCompetition) {
|
||||
|
||||
org.bukkit.World mapWorld = spleefCompetition.getMap().getWorld();
|
||||
String worldName = mapWorld == null ? null : mapWorld.getName();
|
||||
if (!ArenaSpleef.getInstance().getMainConfig().isWorldAllowed(worldName)) {
|
||||
|
||||
ArenaSpleef.getInstance().getSLF4JLogger().warn(
|
||||
"Refusing to start Spleef competition '{}' — world '{}' is not in the Spleef world whitelist.",
|
||||
spleefCompetition.getMap().getName(), worldName);
|
||||
this.leaveWorldBlockedCompetition(spleefCompetition, worldName);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!spleefCompetition.isMatchRegionAllowed()) {
|
||||
|
||||
ArenaSpleef.getInstance().getSLF4JLogger().warn(
|
||||
"Refusing to start Spleef competition '{}' — map is not inside a whitelisted WorldGuard region.",
|
||||
spleefCompetition.getMap().getName());
|
||||
this.leaveRegionBlockedCompetition(spleefCompetition);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onBlockBreak(BlockBreakEvent event, ArenaPlayer player) {
|
||||
|
||||
// Ensure that the game is the classic spleef game
|
||||
if (this.game != SpleefGame.CLASSIC) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (player.getCompetition().option(ArenaOptionType.BLOCK_BREAK).map(BooleanArenaOption::isEnabled)
|
||||
.orElse(true))
|
||||
{
|
||||
|
||||
return; // Game already allows block breaking
|
||||
|
||||
}
|
||||
|
||||
SpleefMap spleefMap = (SpleefMap) player.getCompetition().getMap();
|
||||
BlockPosition pos = event.getBlock().getLocation().toBlock();
|
||||
SpleefMap.Layer layer = spleefMap.getLayer(pos);
|
||||
if (layer != null) {
|
||||
|
||||
if (!(player.getCompetition() instanceof SpleefCompetition competition)
|
||||
|| !competition.isLocationAllowed(event.getBlock().getLocation()))
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(false);
|
||||
this.giveSnowballsFromFloor(event);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onMove(PlayerMoveEvent event, SpleefCompetition competition) {
|
||||
|
||||
if (event.getFrom().getBlockX() == event.getTo().getBlockX()
|
||||
&& event.getFrom().getBlockY() == event.getTo().getBlockY()
|
||||
&& event.getFrom().getBlockZ() == event.getTo().getBlockZ())
|
||||
{
|
||||
|
||||
return; // Same block, skip hot-path work
|
||||
|
||||
}
|
||||
|
||||
SpleefMap spleefMap = (SpleefMap) competition.getMap();
|
||||
if (spleefMap.getDeathRegion() != null && spleefMap.getDeathRegion().isInside(event.getTo())) {
|
||||
|
||||
event.getPlayer().damage(10000.0D); // Kill the player
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// For the events below:
|
||||
// We'd only ever capture this event if the conditions in the event resolvers
|
||||
// are met, so we can safely run the below code without having to check for a
|
||||
// lot of conditions that would otherwise be necessary.
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onProjectileHit(ProjectileHitEvent event, SpleefCompetition competition) {
|
||||
|
||||
Block block = event.getHitBlock();
|
||||
SpleefMap spleefMap = (SpleefMap) competition.getMap();
|
||||
|
||||
BlockPosition pos = block.getLocation().toBlock();
|
||||
SpleefMap.Layer layer = spleefMap.getLayer(pos);
|
||||
if (layer == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!competition.isLocationAllowed(block.getLocation())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
block.setType(Material.AIR);
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onTntPrime(TNTPrimeEvent event) {
|
||||
|
||||
event.setCancelled(true);
|
||||
if (event.getCause() == TNTPrimeEvent.PrimeCause.PROJECTILE) {
|
||||
|
||||
if (event.getPrimingEntity() != null && !(event.getPrimingEntity() instanceof Player)) {
|
||||
|
||||
event.getPrimingEntity().remove();
|
||||
|
||||
}
|
||||
|
||||
event.getBlock().setType(Material.AIR);
|
||||
TNTPrimed tntEntity = event.getBlock().getWorld().spawn(event.getBlock().getLocation().toCenterLocation(),
|
||||
TNTPrimed.class, tnt ->
|
||||
{
|
||||
|
||||
tnt.setFuseTicks(Integer.MAX_VALUE);
|
||||
|
||||
});
|
||||
Bukkit.getScheduler().runTaskLater(ArenaSpleef.getInstance(), tntEntity::remove, 20);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void giveSnowballsFromFloor(BlockBreakEvent event) {
|
||||
|
||||
int amount = this.getSnowballDropCount(event.getBlock());
|
||||
if (amount <= 0) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setDropItems(false);
|
||||
event.getPlayer().getInventory().addItem(new ItemStack(Material.SNOWBALL, amount));
|
||||
|
||||
}
|
||||
|
||||
private int getSnowballDropCount(Block block) {
|
||||
|
||||
if (block.getType() == Material.SNOW_BLOCK) {
|
||||
|
||||
return 4;
|
||||
|
||||
}
|
||||
|
||||
if (block.getType() != Material.SNOW) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (blockData instanceof Snow snow) {
|
||||
|
||||
return snow.getLayers();
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
private void leaveWorldBlockedCompetition(SpleefCompetition competition, String worldName) {
|
||||
|
||||
List<ArenaPlayer> players = new ArrayList<>(competition.getPlayers());
|
||||
players.addAll(competition.getSpectators());
|
||||
for (ArenaPlayer player : players) {
|
||||
|
||||
SpleefMessages.WORLD_BLOCKED_AT_RUNTIME.send(player.getPlayer(), worldName);
|
||||
competition.leave(player, ArenaLeaveEvent.Cause.PLUGIN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void leaveRegionBlockedCompetition(SpleefCompetition competition) {
|
||||
|
||||
List<ArenaPlayer> players = new ArrayList<>(competition.getPlayers());
|
||||
players.addAll(competition.getSpectators());
|
||||
for (ArenaPlayer player : players) {
|
||||
|
||||
SpleefMessages.MATCH_REGION_NOT_WHITELISTED.send(player.getPlayer());
|
||||
competition.leave(player, ArenaLeaveEvent.Cause.PLUGIN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SpleefGame getGame() {
|
||||
|
||||
return this.game;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
import org.battleplugins.arena.competition.CompetitionType;
|
||||
import org.battleplugins.arena.competition.JoinResult;
|
||||
import org.battleplugins.arena.competition.LiveCompetition;
|
||||
import org.battleplugins.arena.competition.PlayerRole;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.spleef.ArenaSpleef;
|
||||
import org.battleplugins.arena.spleef.SpleefConfig;
|
||||
import org.battleplugins.arena.spleef.SpleefMessages;
|
||||
import org.battleplugins.arena.spleef.WorldGuardSupport;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SpleefCompetition extends LiveCompetition<SpleefCompetition> {
|
||||
|
||||
private static final JoinResult REGION_NOT_JOINABLE = new JoinResult(false,
|
||||
SpleefMessages.MATCH_REGION_NOT_WHITELISTED);
|
||||
|
||||
private final SpleefMap map;
|
||||
|
||||
public SpleefCompetition(SpleefArena arena, CompetitionType type, SpleefMap map) {
|
||||
|
||||
super(arena, type, map);
|
||||
|
||||
this.map = map;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<JoinResult> canJoin(Collection<Player> players, PlayerRole role) {
|
||||
|
||||
if (!this.isMatchRegionAllowed()) {
|
||||
|
||||
return CompletableFuture.completedFuture(REGION_NOT_JOINABLE);
|
||||
|
||||
}
|
||||
|
||||
return super.canJoin(players, role);
|
||||
|
||||
}
|
||||
|
||||
public boolean isMatchRegionAllowed() {
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config == null || !config.hasRegionWhitelist()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
World world = this.map.getWorld();
|
||||
if (world == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
String worldGuardRegion = this.map.getWorldGuardRegion();
|
||||
if (config.isRegionAllowed(worldGuardRegion) && WorldGuardSupport.regionExists(world, worldGuardRegion)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
List<SpleefMap.Layer> layers = this.map.getLayers();
|
||||
if (!layers.isEmpty()) {
|
||||
|
||||
return layers.stream().allMatch(layer -> this.isBoundsInAllowedRegion(layer.getBounds()));
|
||||
|
||||
}
|
||||
|
||||
return this.map.bounds().map(this::isBoundsInAllowedRegion).orElse(false);
|
||||
|
||||
}
|
||||
|
||||
public boolean isLocationAllowed(Location location) {
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
return config == null || config.isRegionAllowedAt(location);
|
||||
|
||||
}
|
||||
|
||||
public void pasteLayers() {
|
||||
|
||||
List<SpleefMap.Layer> layers = this.map.getLayers();
|
||||
for (SpleefMap.Layer layer : layers) {
|
||||
|
||||
for (int x = layer.getBounds().getMinX(); x <= layer.getBounds().getMaxX(); x++) {
|
||||
|
||||
for (int y = layer.getBounds().getMinY(); y <= layer.getBounds().getMaxY(); y++) {
|
||||
|
||||
for (int z = layer.getBounds().getMinZ(); z <= layer.getBounds().getMaxZ(); z++) {
|
||||
|
||||
Block block = this.map.getWorld().getBlockAt(x, y, z);
|
||||
if (!this.isLocationAllowed(block.getLocation())) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
block.setBlockData(layer.getBlockData());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isBoundsInAllowedRegion(Bounds bounds) {
|
||||
|
||||
World world = this.map.getWorld();
|
||||
if (world == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMinY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMinY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMaxY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMaxY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMinY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMinY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMaxY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMaxY(), bounds.getMaxZ()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
public enum SpleefGame {
|
||||
CLASSIC, BOW_SPLEEF
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.competition.LiveCompetition;
|
||||
import org.battleplugins.arena.competition.map.LiveCompetitionMap;
|
||||
import org.battleplugins.arena.competition.map.MapFactory;
|
||||
import org.battleplugins.arena.competition.map.MapType;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.competition.map.options.Spawns;
|
||||
import org.battleplugins.arena.config.ArenaOption;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SpleefMap extends LiveCompetitionMap {
|
||||
|
||||
static final MapFactory FACTORY = MapFactory.create(SpleefMap.class, SpleefMap::new);
|
||||
|
||||
@ArenaOption(name = "layers", description = "The layers for this spleef map.")
|
||||
private List<Layer> layers;
|
||||
|
||||
@ArenaOption(name = "death-region", description = "The region where players will die if they fall into.")
|
||||
private Bounds deathRegion;
|
||||
|
||||
@ArenaOption(name = "worldguard-region", description = "The WorldGuard region ID used for match region checks and Bow Spleef TNT.")
|
||||
private String worldGuardRegion;
|
||||
|
||||
private final Map<Position, Layer> positionToLayers = new HashMap<>();
|
||||
|
||||
public SpleefMap() {
|
||||
|
||||
}
|
||||
|
||||
public SpleefMap(String name, Arena arena, MapType type, String world, @Nullable Bounds bounds,
|
||||
@Nullable Spawns spawns)
|
||||
{
|
||||
|
||||
super(name, arena, type, world, bounds, spawns);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess() {
|
||||
|
||||
super.postProcess();
|
||||
|
||||
if (this.layers != null) {
|
||||
|
||||
for (Layer layer : this.layers) {
|
||||
|
||||
indexLayer(layer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void indexLayer(Layer layer) {
|
||||
|
||||
Bounds bounds = layer.getBounds();
|
||||
for (int x = bounds.getMinX(); x <= bounds.getMaxX(); x++) {
|
||||
|
||||
for (int y = bounds.getMinY(); y <= bounds.getMaxY(); y++) {
|
||||
|
||||
for (int z = bounds.getMinZ(); z <= bounds.getMaxZ(); z++) {
|
||||
|
||||
this.positionToLayers.put(Position.block(x, y, z), layer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void unindexLayer(Layer layer) {
|
||||
|
||||
Iterator<Map.Entry<Position, Layer>> it = this.positionToLayers.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
|
||||
if (it.next().getValue() == layer) {
|
||||
|
||||
it.remove();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveCompetition<SpleefCompetition> createCompetition(Arena arena) {
|
||||
|
||||
if (!(arena instanceof SpleefArena spleefArena)) {
|
||||
|
||||
throw new IllegalArgumentException("Arena must be a spleef arena!");
|
||||
|
||||
}
|
||||
|
||||
return new SpleefCompetition(spleefArena, arena.getType(), this);
|
||||
|
||||
}
|
||||
|
||||
public List<Layer> getLayers() {
|
||||
|
||||
return this.layers == null ? List.of() : List.copyOf(this.layers);
|
||||
|
||||
}
|
||||
|
||||
public void addLayer(Layer layer) {
|
||||
|
||||
if (this.layers == null) {
|
||||
|
||||
this.layers = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
this.layers.add(layer);
|
||||
indexLayer(layer);
|
||||
|
||||
}
|
||||
|
||||
public void addLayer(int index, Layer layer) {
|
||||
|
||||
if (this.layers == null) {
|
||||
|
||||
this.layers = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
this.layers.add(index, layer);
|
||||
indexLayer(layer);
|
||||
|
||||
}
|
||||
|
||||
public void removeLayer(Layer layer) {
|
||||
|
||||
if (this.layers == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.layers.remove(layer);
|
||||
unindexLayer(layer);
|
||||
|
||||
}
|
||||
|
||||
public void clearLayers() {
|
||||
|
||||
if (this.layers == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.layers.clear();
|
||||
this.positionToLayers.clear();
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Layer getLayer(Position position) {
|
||||
|
||||
return this.positionToLayers.get(position);
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bounds getDeathRegion() {
|
||||
|
||||
return this.deathRegion;
|
||||
|
||||
}
|
||||
|
||||
public void setDeathRegion(Bounds deathRegion) {
|
||||
|
||||
this.deathRegion = deathRegion;
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getWorldGuardRegion() {
|
||||
|
||||
return this.worldGuardRegion;
|
||||
|
||||
}
|
||||
|
||||
public void setWorldGuardRegion(String worldGuardRegion) {
|
||||
|
||||
this.worldGuardRegion = worldGuardRegion;
|
||||
|
||||
}
|
||||
|
||||
public static class Layer {
|
||||
|
||||
@ArenaOption(name = "bounds", description = "The bounds of this layer.", required = true)
|
||||
private Bounds bounds;
|
||||
|
||||
@ArenaOption(name = "block-data", description = "The block data of this layer.", required = true)
|
||||
private BlockData blockData;
|
||||
|
||||
public Layer() {
|
||||
|
||||
}
|
||||
|
||||
public Layer(Bounds bounds, BlockData blockData) {
|
||||
|
||||
this.bounds = bounds;
|
||||
this.blockData = blockData;
|
||||
|
||||
}
|
||||
|
||||
public Bounds getBounds() {
|
||||
|
||||
return this.bounds;
|
||||
|
||||
}
|
||||
|
||||
public BlockData getBlockData() {
|
||||
|
||||
return this.blockData;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.editor;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.editor.ArenaEditorWizard;
|
||||
import org.battleplugins.arena.editor.EditorContext;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class DeathRegionContext extends EditorContext<DeathRegionContext> {
|
||||
|
||||
private SpleefMap map;
|
||||
private Position min;
|
||||
private Position max;
|
||||
|
||||
public DeathRegionContext(ArenaEditorWizard<DeathRegionContext> wizard, Arena arena, Player player) {
|
||||
|
||||
super(wizard, arena, player);
|
||||
|
||||
}
|
||||
|
||||
public SpleefMap getMap() {
|
||||
|
||||
return this.map;
|
||||
|
||||
}
|
||||
|
||||
public void setMap(SpleefMap map) {
|
||||
|
||||
this.map = map;
|
||||
|
||||
}
|
||||
|
||||
public Position getMin() {
|
||||
|
||||
return this.min;
|
||||
|
||||
}
|
||||
|
||||
public void setMin(Position min) {
|
||||
|
||||
this.min = min;
|
||||
|
||||
}
|
||||
|
||||
public Position getMax() {
|
||||
|
||||
return this.max;
|
||||
|
||||
}
|
||||
|
||||
public void setMax(Position max) {
|
||||
|
||||
this.max = max;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
|
||||
return this.map != null && this.min != null && this.max != null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.editor;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.editor.ArenaEditorWizard;
|
||||
import org.battleplugins.arena.editor.EditorContext;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class LayerContext extends EditorContext<LayerContext> {
|
||||
|
||||
private SpleefMap map;
|
||||
private Position min;
|
||||
private Position max;
|
||||
private BlockData blockData;
|
||||
|
||||
public LayerContext(ArenaEditorWizard<LayerContext> wizard, Arena arena, Player player) {
|
||||
|
||||
super(wizard, arena, player);
|
||||
|
||||
}
|
||||
|
||||
public SpleefMap getMap() {
|
||||
|
||||
return this.map;
|
||||
|
||||
}
|
||||
|
||||
public void setMap(SpleefMap map) {
|
||||
|
||||
this.map = map;
|
||||
|
||||
}
|
||||
|
||||
public Position getMin() {
|
||||
|
||||
return this.min;
|
||||
|
||||
}
|
||||
|
||||
public void setMin(Position min) {
|
||||
|
||||
this.min = min;
|
||||
|
||||
}
|
||||
|
||||
public Position getMax() {
|
||||
|
||||
return this.max;
|
||||
|
||||
}
|
||||
|
||||
public void setMax(Position max) {
|
||||
|
||||
this.max = max;
|
||||
|
||||
}
|
||||
|
||||
public BlockData getBlockData() {
|
||||
|
||||
return this.blockData;
|
||||
|
||||
}
|
||||
|
||||
public void setBlockData(BlockData blockData) {
|
||||
|
||||
this.blockData = blockData;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
|
||||
return this.map != null && this.min != null && this.max != null && this.blockData != null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.editor;
|
||||
|
||||
import org.battleplugins.arena.editor.type.EditorKey;
|
||||
|
||||
public enum LayerOption implements EditorKey {
|
||||
|
||||
BLOCK_DATA("blockData");
|
||||
|
||||
private final String key;
|
||||
|
||||
LayerOption(String key) {
|
||||
|
||||
this.key = key;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
|
||||
return this.key;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.editor;
|
||||
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.config.ParseException;
|
||||
import org.battleplugins.arena.editor.ArenaEditorWizard;
|
||||
import org.battleplugins.arena.editor.ArenaEditorWizards;
|
||||
import org.battleplugins.arena.editor.stage.PositionInputStage;
|
||||
import org.battleplugins.arena.editor.stage.TextInputStage;
|
||||
import org.battleplugins.arena.editor.type.MapOption;
|
||||
import org.battleplugins.arena.messages.Messages;
|
||||
import org.battleplugins.arena.spleef.SpleefMessages;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class SpleefEditorWizards {
|
||||
|
||||
public static final ArenaEditorWizard<LayerContext> ADD_LAYER = ArenaEditorWizards.createWizard(LayerContext::new)
|
||||
.addStage(MapOption.MIN_POS,
|
||||
new PositionInputStage<>(SpleefMessages.LAYER_SET_MIN_POSITION, ctx -> ctx::setMin))
|
||||
.addStage(MapOption.MAX_POS,
|
||||
new PositionInputStage<>(SpleefMessages.LAYER_SET_MAX_POSITION, ctx -> ctx::setMax))
|
||||
.addStage(LayerOption.BLOCK_DATA, new TextInputStage<>(SpleefMessages.LAYER_SET_BLOCK_DATA,
|
||||
SpleefMessages.LAYER_SET_BLOCK_DATA_INVALID, (ctx, str) ->
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
Bukkit.createBlockData(str);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}, ctx -> str -> ctx.setBlockData(Bukkit.createBlockData(str)))).onCreationComplete(ctx -> {
|
||||
|
||||
Bounds bounds = new Bounds(ctx.getMin(), ctx.getMax());
|
||||
|
||||
SpleefMap map = ctx.getMap();
|
||||
SpleefMap.Layer layer = new SpleefMap.Layer(bounds, ctx.getBlockData());
|
||||
map.addLayer(layer);
|
||||
|
||||
try {
|
||||
|
||||
map.save();
|
||||
|
||||
} catch (ParseException | IOException e) {
|
||||
|
||||
BattleArena.getInstance().error("Failed to save map file for arena {}",
|
||||
ctx.getArena().getName(), e);
|
||||
Messages.MAP_FAILED_TO_SAVE.send(ctx.getPlayer(), map.getName());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefMessages.LAYER_ADDED.send(ctx.getPlayer());
|
||||
|
||||
});
|
||||
|
||||
public static final ArenaEditorWizard<DeathRegionContext> DEATH_REGION = ArenaEditorWizards
|
||||
.createWizard(DeathRegionContext::new)
|
||||
.addStage(MapOption.MIN_POS,
|
||||
new PositionInputStage<>(SpleefMessages.DEATH_REGION_SET_MIN_POSITION, ctx -> ctx::setMin))
|
||||
.addStage(MapOption.MAX_POS,
|
||||
new PositionInputStage<>(SpleefMessages.DEATH_REGION_SET_MAX_POSITION, ctx -> ctx::setMax))
|
||||
.onCreationComplete(ctx ->
|
||||
{
|
||||
|
||||
Bounds bounds = new Bounds(ctx.getMin(), ctx.getMax());
|
||||
|
||||
SpleefMap map = ctx.getMap();
|
||||
map.setDeathRegion(bounds);
|
||||
|
||||
try {
|
||||
|
||||
map.save();
|
||||
|
||||
} catch (ParseException | IOException e) {
|
||||
|
||||
BattleArena.getInstance().error("Failed to save map file for arena {}", ctx.getArena().getName(),
|
||||
e);
|
||||
Messages.MAP_FAILED_TO_SAVE.send(ctx.getPlayer(), map.getName());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefMessages.DEATH_REGION_SET.send(ctx.getPlayer());
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.hook;
|
||||
|
||||
import org.battleplugins.arena.event.player.ArenaJoinEvent;
|
||||
import org.battleplugins.arena.event.player.ArenaLeaveEvent;
|
||||
import org.battleplugins.arena.event.player.ArenaSpectateEvent;
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefCompetition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class GameModeInventoriesListener implements Listener {
|
||||
|
||||
public static final String PLUGIN_NAME = "GameModeInventories-OG";
|
||||
|
||||
private static final String USE_PERMISSION = "gamemodeinventories.use";
|
||||
|
||||
private final Plugin owner;
|
||||
private final Map<UUID, PermissionAttachment> attachments = new HashMap<>();
|
||||
|
||||
private GameModeInventoriesListener(Plugin owner) {
|
||||
|
||||
this.owner = owner;
|
||||
|
||||
}
|
||||
|
||||
public static GameModeInventoriesListener register(Plugin owner) {
|
||||
|
||||
final Plugin gameModeInventories = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME);
|
||||
if (gameModeInventories == null || !gameModeInventories.isEnabled()) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
final GameModeInventoriesListener listener = new GameModeInventoriesListener(owner);
|
||||
Bukkit.getPluginManager().registerEvents(listener, owner);
|
||||
return listener;
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onArenaJoin(ArenaJoinEvent event) {
|
||||
|
||||
if (event.getArenaPlayer().getCompetition() instanceof SpleefCompetition) {
|
||||
|
||||
this.suspend(event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onArenaSpectate(ArenaSpectateEvent event) {
|
||||
|
||||
if (event.getArenaPlayer().getCompetition() instanceof SpleefCompetition) {
|
||||
|
||||
this.suspend(event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onArenaLeave(ArenaLeaveEvent event) {
|
||||
|
||||
if (!(event.getArenaPlayer().getCompetition() instanceof SpleefCompetition)) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
final Player player = event.getPlayer();
|
||||
Bukkit.getScheduler().runTask(this.owner, () -> {
|
||||
|
||||
if (!SpleefAPI.isInSpleef(player)) {
|
||||
|
||||
this.release(player);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void releaseAll() {
|
||||
|
||||
for (PermissionAttachment attachment : this.attachments.values()) {
|
||||
|
||||
try {
|
||||
|
||||
attachment.remove();
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.attachments.clear();
|
||||
|
||||
}
|
||||
|
||||
private void suspend(Player player) {
|
||||
|
||||
// Keep GMI from saving arena-cleared contents over real inventories.
|
||||
this.attachments.computeIfAbsent(player.getUniqueId(),
|
||||
key -> player.addAttachment(this.owner, USE_PERMISSION, false));
|
||||
|
||||
}
|
||||
|
||||
private void release(Player player) {
|
||||
|
||||
final PermissionAttachment attachment = this.attachments.remove(player.getUniqueId());
|
||||
if (attachment == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
attachment.remove();
|
||||
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.battleplugins.arena.spleef.hook;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventException;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.EventExecutor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class PlayerBountiesListener implements Listener {
|
||||
|
||||
public static final String PLUGIN_NAME = "PlayerBounties-OG";
|
||||
|
||||
private static final String CLAIM_EVENT_CLASS = "com.tcoded.playerbountiesplus.event.BountyClaimEvent";
|
||||
private static final String SET_EVENT_CLASS = "com.tcoded.playerbountiesplus.event.BountySetEvent";
|
||||
|
||||
private PlayerBountiesListener() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean register(Plugin owner) {
|
||||
|
||||
Plugin bounties = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME);
|
||||
if (bounties == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
Class<? extends Event> claimEventClass;
|
||||
Class<? extends Event> setEventClass;
|
||||
Method getClaimant;
|
||||
Method getVictim;
|
||||
Method getTarget;
|
||||
|
||||
try {
|
||||
|
||||
claimEventClass = Class.forName(CLAIM_EVENT_CLASS).asSubclass(Event.class);
|
||||
setEventClass = Class.forName(SET_EVENT_CLASS).asSubclass(Event.class);
|
||||
getClaimant = claimEventClass.getMethod("getClaimant");
|
||||
getVictim = claimEventClass.getMethod("getVictim");
|
||||
getTarget = setEventClass.getMethod("getTarget");
|
||||
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | ClassCastException ex) {
|
||||
|
||||
owner.getSLF4JLogger().warn(
|
||||
"PlayerBounties-OG is present but the expected API is missing; skipping bounty protection.", ex);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (!Cancellable.class.isAssignableFrom(claimEventClass)
|
||||
|| !Cancellable.class.isAssignableFrom(setEventClass))
|
||||
{
|
||||
|
||||
owner.getSLF4JLogger().warn("PlayerBounties-OG events are not Cancellable; skipping bounty protection.");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
PlayerBountiesListener listener = new PlayerBountiesListener();
|
||||
|
||||
Bukkit.getPluginManager().registerEvent(claimEventClass, listener, EventPriority.NORMAL, new EventExecutor() {
|
||||
|
||||
@Override
|
||||
public void execute(Listener listener, Event event) throws EventException {
|
||||
|
||||
cancelIfInSpleef(event, getClaimant, getVictim);
|
||||
|
||||
}
|
||||
|
||||
}, owner, true);
|
||||
|
||||
Bukkit.getPluginManager().registerEvent(setEventClass, listener, EventPriority.NORMAL, new EventExecutor() {
|
||||
|
||||
@Override
|
||||
public void execute(Listener listener, Event event) throws EventException {
|
||||
|
||||
cancelIfInSpleef(event, getTarget);
|
||||
|
||||
}
|
||||
|
||||
}, owner, true);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private static void cancelIfInSpleef(Event event, Method... playerAccessors) throws EventException {
|
||||
|
||||
for (Method accessor : playerAccessors) {
|
||||
|
||||
Player player;
|
||||
|
||||
try {
|
||||
|
||||
player = (Player) accessor.invoke(event);
|
||||
|
||||
} catch (ReflectiveOperationException | ClassCastException ex) {
|
||||
|
||||
throw new EventException(ex);
|
||||
|
||||
}
|
||||
|
||||
if (SpleefAPI.isInSpleef(player)) {
|
||||
|
||||
((Cancellable) event).setCancelled(true);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
name: Spleef
|
||||
type: Match
|
||||
# Optional. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: spleef_classic
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
team-amount: 2+
|
||||
team-selection: none
|
||||
modules:
|
||||
- scoreboards
|
||||
lives:
|
||||
enabled: false
|
||||
victory-conditions:
|
||||
teams-alive:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
game: classic
|
||||
events:
|
||||
on-join:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- flight{enabled=false}
|
||||
- teleport{location=waitroom}
|
||||
on-spectate:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=spectator}
|
||||
- flight{enabled=true}
|
||||
- teleport{location=spectator}
|
||||
on-leave:
|
||||
- clear-effects
|
||||
- restore{types=all}
|
||||
- remove-scoreboard
|
||||
on-death:
|
||||
- clear-inventory
|
||||
- respawn
|
||||
- delay{ticks=1}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- teleport{location=waitroom}
|
||||
options:
|
||||
- block-break{enabled=false}
|
||||
- block-place{enabled=false}
|
||||
- block-drops{enabled=false}
|
||||
- block-interact{enabled=false}
|
||||
- item-drops{enabled=false}
|
||||
- keep-inventory{enabled=true}
|
||||
- keep-experience{enabled=true}
|
||||
- damage-entities{option=never}
|
||||
- damage-players{option=never}
|
||||
initial-phase: waiting
|
||||
phases:
|
||||
waiting:
|
||||
allow-join: true
|
||||
next-phase: waitingforplayers
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
- paste-layers
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
waitingforplayers:
|
||||
allow-join: true
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: countdown
|
||||
countdown-time: 45s
|
||||
events:
|
||||
on-start:
|
||||
- broadcast{message=A spleef game is about to start! Do <aqua>/spleef join</aqua> to join;type=chat;audience=server}
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-complete:
|
||||
- teleport{location=team_spawn}
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
countdown:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: ingame
|
||||
countdown-time: 5s
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
- give-effects{effects=[slowness{duration=100;amplifier=255}]}
|
||||
on-complete:
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
- clear-effects
|
||||
ingame:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
next-phase: victory
|
||||
options:
|
||||
- block-interact{enabled=true}
|
||||
events:
|
||||
on-start:
|
||||
- change-gamemode{gamemode=survival}
|
||||
- give-shovel{shovel=classic}
|
||||
- apply-scoreboard{scoreboard=ingame-list-alive}
|
||||
victory:
|
||||
allow-join: false
|
||||
allow-spectate: false
|
||||
next-phase: waiting
|
||||
duration: 5s
|
||||
events:
|
||||
on-complete:
|
||||
- leave
|
||||
- remove-scoreboard
|
||||
on-victory:
|
||||
- send-message{message=<green>Congrats, you won!</green>}
|
||||
- play-sound{sound=entity.player.levelup;pitch=1;volume=1}
|
||||
on-lose:
|
||||
- send-message{message=<red>Sorry, you lost!</red>}
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
@@ -1,22 +1,26 @@
|
||||
# -----------------
|
||||
# Main configuration for ArenaSpleef
|
||||
#
|
||||
# Documentation: https://docs.battleplugins.org/books/additional-gamemodes/chapter/spleef
|
||||
# Support: https://discord.gg/tMVPVJf
|
||||
# GitHub: https://github.com/BattlePlugins/ArenaSpleef
|
||||
# -----------------
|
||||
config-version: 2.0 # The config version, do not change!
|
||||
|
||||
# Worlds where Spleef arenas may be created and where matches may run.
|
||||
# Leave empty to allow every world. Names are matched case-insensitively.
|
||||
# Worlds where Spleef arenas may be created and matches may run.
|
||||
# Spleef never copies worlds; MyWorlds is only used to ensure managed worlds load first.
|
||||
world-whitelist:
|
||||
- world
|
||||
|
||||
# WorldGuard regions where Spleef arenas may be created and matches may modify blocks.
|
||||
# Leave empty to allow any region in an allowed world.
|
||||
region-whitelist: []
|
||||
match:
|
||||
minimum-players: 2
|
||||
waiting-seconds: 20
|
||||
time-limit-seconds: 300
|
||||
victory-seconds: 5
|
||||
|
||||
# The tools to use for Spleef.
|
||||
shovels:
|
||||
classic: diamond_shovel{display-name=<yellow>Spleef Shovel}
|
||||
bow: bow{display-name=<red><bold>Fire Bow;enchantments=[unbreaking:10,infinity:1,flame:1]}
|
||||
items:
|
||||
classic:
|
||||
material: DIAMOND_SHOVEL
|
||||
name: '&eSpleef Shovel'
|
||||
bow:
|
||||
material: BOW
|
||||
name: '&c&lFire Bow'
|
||||
enchantments:
|
||||
ARROW_INFINITE: 1
|
||||
ARROW_FIRE: 1
|
||||
DURABILITY: 10
|
||||
|
||||
scoreboard:
|
||||
enabled: true
|
||||
title: '&b&lSPLEEF'
|
||||
@@ -1,9 +1,22 @@
|
||||
name: Spleef-OG
|
||||
main: org.battleplugins.arena.spleef.ArenaSpleef
|
||||
main: net.trueog.spleefog.SpleefPlugin
|
||||
version: ${version}
|
||||
description: Classic and Bow Spleef modes for BattleArena WorldGuard regions.
|
||||
author: BattlePlugins
|
||||
website: https://battleplugins.org
|
||||
description: Classic and Bow Spleef in WorldGuard regions.
|
||||
author: TrueOG
|
||||
website: https://github.com/true-og/Spleef-OG
|
||||
api-version: ${apiVersion}
|
||||
depend: [BattleArena, WorldGuard]
|
||||
softdepend: [GameModeInventories-OG, HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit]
|
||||
depend: [WorldGuard]
|
||||
softdepend: [MyWorlds, GameModeInventories-OG, PlayerBounties-OG]
|
||||
commands:
|
||||
spleef:
|
||||
description: Join, spectate, and manage Spleef arenas.
|
||||
usage: /spleef
|
||||
permissions:
|
||||
spleef.play:
|
||||
default: true
|
||||
spleef.spectate:
|
||||
default: true
|
||||
spleef.stats:
|
||||
default: true
|
||||
spleef.admin:
|
||||
default: op
|
||||
@@ -1,104 +0,0 @@
|
||||
name: BowSpleef
|
||||
mode: Spleef
|
||||
type: Match
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
team-amount: 2+
|
||||
team-selection: none
|
||||
modules:
|
||||
- scoreboards
|
||||
lives:
|
||||
enabled: false
|
||||
victory-conditions:
|
||||
teams-alive:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
game: bow_spleef
|
||||
# Required for Bow Spleef TNT handling. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: bowspleef
|
||||
events:
|
||||
on-join:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- flight{enabled=false}
|
||||
- teleport{location=waitroom}
|
||||
on-spectate:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=spectator}
|
||||
- flight{enabled=true}
|
||||
- teleport{location=spectator}
|
||||
on-leave:
|
||||
- clear-effects
|
||||
- restore{types=all}
|
||||
- remove-scoreboard
|
||||
on-death:
|
||||
- clear-inventory
|
||||
- respawn
|
||||
- delay{ticks=1}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- teleport{location=waitroom}
|
||||
options:
|
||||
- block-break{enabled=false}
|
||||
- block-place{enabled=false}
|
||||
- block-drops{enabled=false}
|
||||
- block-interact{enabled=false}
|
||||
- item-drops{enabled=false}
|
||||
- keep-inventory{enabled=true}
|
||||
- keep-experience{enabled=true}
|
||||
- damage-entities{option=never}
|
||||
- damage-players{option=never}
|
||||
initial-phase: waiting
|
||||
phases:
|
||||
waiting:
|
||||
allow-join: true
|
||||
next-phase: countdown
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
countdown:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: ingame
|
||||
countdown-time: 5s
|
||||
events:
|
||||
on-start:
|
||||
- paste-layers
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-complete:
|
||||
- teleport{location=team_spawn}
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
ingame:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
next-phase: victory
|
||||
options:
|
||||
- block-interact{enabled=true}
|
||||
events:
|
||||
on-start:
|
||||
- give-shovel{shovel=bow}
|
||||
- give-item{item=arrow}
|
||||
- change-gamemode{gamemode=survival}
|
||||
- apply-scoreboard{scoreboard=ingame-list-alive}
|
||||
victory:
|
||||
allow-join: false
|
||||
allow-spectate: false
|
||||
next-phase: waiting
|
||||
duration: 5s
|
||||
events:
|
||||
on-complete:
|
||||
- leave
|
||||
- remove-scoreboard
|
||||
on-victory:
|
||||
- send-message{message=<green>Congrats, you won!</green>}
|
||||
- play-sound{sound=entity.player.levelup;pitch=1;volume=1}
|
||||
on-lose:
|
||||
- send-message{message=<red>Sorry, you lost!</red>}
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
@@ -1,118 +0,0 @@
|
||||
name: Spleef
|
||||
type: Match
|
||||
# Optional. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: spleef_classic
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
team-amount: 2+
|
||||
team-selection: none
|
||||
modules:
|
||||
- scoreboards
|
||||
lives:
|
||||
enabled: false
|
||||
victory-conditions:
|
||||
teams-alive:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
game: classic
|
||||
events:
|
||||
on-join:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- flight{enabled=false}
|
||||
- teleport{location=waitroom}
|
||||
on-spectate:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=spectator}
|
||||
- flight{enabled=true}
|
||||
- teleport{location=spectator}
|
||||
on-leave:
|
||||
- clear-effects
|
||||
- restore{types=all}
|
||||
- remove-scoreboard
|
||||
on-death:
|
||||
- clear-inventory
|
||||
- respawn
|
||||
- delay{ticks=1}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- teleport{location=waitroom}
|
||||
options:
|
||||
- block-break{enabled=false}
|
||||
- block-place{enabled=false}
|
||||
- block-drops{enabled=false}
|
||||
- block-interact{enabled=false}
|
||||
- item-drops{enabled=false}
|
||||
- keep-inventory{enabled=true}
|
||||
- keep-experience{enabled=true}
|
||||
- damage-entities{option=never}
|
||||
- damage-players{option=never}
|
||||
initial-phase: waiting
|
||||
phases:
|
||||
waiting:
|
||||
allow-join: true
|
||||
next-phase: waitingforplayers
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
- paste-layers
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
waitingforplayers:
|
||||
allow-join: true
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: countdown
|
||||
countdown-time: 45s
|
||||
events:
|
||||
on-start:
|
||||
- broadcast{message=A spleef game is about to start! Do <aqua>/spleef join</aqua> to join;type=chat;audience=server}
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-complete:
|
||||
- teleport{location=team_spawn}
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
countdown:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: ingame
|
||||
countdown-time: 5s
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
- give-effects{effects=[slowness{duration=100;amplifier=255}]}
|
||||
on-complete:
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
- clear-effects
|
||||
ingame:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
next-phase: victory
|
||||
options:
|
||||
- block-interact{enabled=true}
|
||||
events:
|
||||
on-start:
|
||||
- change-gamemode{gamemode=survival}
|
||||
- give-shovel{shovel=classic}
|
||||
- apply-scoreboard{scoreboard=ingame-list-alive}
|
||||
victory:
|
||||
allow-join: false
|
||||
allow-spectate: false
|
||||
next-phase: waiting
|
||||
duration: 5s
|
||||
events:
|
||||
on-complete:
|
||||
- leave
|
||||
- remove-scoreboard
|
||||
on-victory:
|
||||
- send-message{message=<green>Congrats, you won!</green>}
|
||||
- play-sound{sound=entity.player.levelup;pitch=1;volume=1}
|
||||
on-lose:
|
||||
- send-message{message=<red>Sorry, you lost!</red>}
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
Reference in new issue
Block a user