Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a665f216ae |
@@ -4,6 +4,7 @@ import io.github.mviper.bomberman.commands.BaseCommand;
|
|||||||
import io.github.mviper.bomberman.game.GamePlayer;
|
import io.github.mviper.bomberman.game.GamePlayer;
|
||||||
import io.github.mviper.bomberman.game.GameSave;
|
import io.github.mviper.bomberman.game.GameSave;
|
||||||
import io.github.mviper.bomberman.game.GameSettings;
|
import io.github.mviper.bomberman.game.GameSettings;
|
||||||
|
import io.github.mviper.bomberman.game.JoinSignListener;
|
||||||
import io.github.mviper.bomberman.utils.DataRestorer;
|
import io.github.mviper.bomberman.utils.DataRestorer;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
@@ -30,7 +31,9 @@ public class Bomberman extends JavaPlugin implements Listener {
|
|||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
ConfigurationSerialization.registerClass(GameSettings.class);
|
ConfigurationSerialization.registerClass(GameSettings.class);
|
||||||
|
ConfigurationSerialization.registerClass(GameSettings.class, "io.github.mdsimmo.bomberman.game.GameSettings");
|
||||||
ConfigurationSerialization.registerClass(DataRestorer.class, "io.github.mviper.bomberman.game.Game$BuildFlags");
|
ConfigurationSerialization.registerClass(DataRestorer.class, "io.github.mviper.bomberman.game.Game$BuildFlags");
|
||||||
|
ConfigurationSerialization.registerClass(DataRestorer.class, "io.github.mdsimmo.bomberman.game.Game$BuildFlags");
|
||||||
|
|
||||||
getDataFolder().mkdirs();
|
getDataFolder().mkdirs();
|
||||||
|
|
||||||
@@ -39,9 +42,9 @@ public class Bomberman extends JavaPlugin implements Listener {
|
|||||||
bukkitBmCmd.setExecutor(bmCmd);
|
bukkitBmCmd.setExecutor(bmCmd);
|
||||||
bukkitBmCmd.setTabCompleter(bmCmd);
|
bukkitBmCmd.setTabCompleter(bmCmd);
|
||||||
|
|
||||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
Bukkit.getPluginManager().registerEvents(new JoinSignListener(), this);
|
||||||
new BmPlaceholder().register();
|
|
||||||
}
|
// Placeholder expansion registration disabled to avoid console startup noise.
|
||||||
|
|
||||||
// Update the old file system
|
// Update the old file system
|
||||||
GameSave.updatePre080Saves();
|
GameSave.updatePre080Saves();
|
||||||
@@ -66,7 +69,9 @@ public class Bomberman extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
// Copy resources
|
// Copy resources
|
||||||
saveResource("config.yml", true);
|
saveResource("config.yml", true);
|
||||||
|
if (!Files.exists(language())) {
|
||||||
saveResource("messages.yml", false);
|
saveResource("messages.yml", false);
|
||||||
|
}
|
||||||
saveResource("default_messages.yml", true);
|
saveResource("default_messages.yml", true);
|
||||||
saveResource("games/templates/purple.game.zip", true);
|
saveResource("games/templates/purple.game.zip", true);
|
||||||
saveResource("games/templates/experimental.game.zip", true);
|
saveResource("games/templates/experimental.game.zip", true);
|
||||||
|
|||||||
@@ -62,11 +62,13 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class Game implements Formattable, Listener {
|
public class Game implements Formattable, Listener {
|
||||||
private static final Bomberman PLUGIN = Bomberman.instance;
|
private static final Bomberman PLUGIN = Bomberman.instance;
|
||||||
|
private static final int AUTO_START_DELAY_SECONDS = 3;
|
||||||
|
|
||||||
private final GameSave save;
|
private final GameSave save;
|
||||||
public final String name;
|
public final String name;
|
||||||
private final Set<Player> players = new HashSet<>();
|
private final Set<Player> players = new HashSet<>();
|
||||||
private boolean running = false;
|
private boolean running = false;
|
||||||
|
private boolean countingDown = false;
|
||||||
private final YamlConfiguration tempData;
|
private final YamlConfiguration tempData;
|
||||||
private Box box;
|
private Box box;
|
||||||
private Set<Location> spawns;
|
private Set<Location> spawns;
|
||||||
@@ -149,6 +151,26 @@ public class Game implements Formattable, Listener {
|
|||||||
return save.origin;
|
return save.origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPlayerCount() {
|
||||||
|
return players.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPlayers() {
|
||||||
|
return getSpawns().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCountingDown() {
|
||||||
|
return countingDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Location location) {
|
||||||
|
return location != null && getBox().contains(location);
|
||||||
|
}
|
||||||
|
|
||||||
public Clipboard getClipboard() throws IOException {
|
public Clipboard getClipboard() throws IOException {
|
||||||
return save.getSchematic();
|
return save.getSchematic();
|
||||||
}
|
}
|
||||||
@@ -193,7 +215,6 @@ public class Game implements Formattable, Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<Location> searchSpawns() {
|
private Set<Location> searchSpawns() {
|
||||||
PLUGIN.getLogger().info("Searching for spawns...");
|
|
||||||
Set<Location> result = new HashSet<>();
|
Set<Location> result = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
Clipboard clipboard = getClipboard();
|
Clipboard clipboard = getClipboard();
|
||||||
@@ -215,7 +236,6 @@ public class Game implements Formattable, Listener {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Failed to search spawns", e);
|
throw new RuntimeException("Failed to search spawns", e);
|
||||||
}
|
}
|
||||||
PLUGIN.getLogger().info(" " + result.size() + " spawns found");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +346,11 @@ public class Game implements Formattable, Listener {
|
|||||||
|
|
||||||
GamePlayer.spawnGamePlayer(event.player, this, gameSpawn);
|
GamePlayer.spawnGamePlayer(event.player, this, gameSpawn);
|
||||||
players.add(event.player);
|
players.add(event.player);
|
||||||
|
|
||||||
|
if (!running && players.size() > 1) {
|
||||||
|
BmRunStartCountDownIntent.startGame(this, AUTO_START_DELAY_SECONDS, false);
|
||||||
|
}
|
||||||
|
|
||||||
event.setHandled();
|
event.setHandled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +445,7 @@ public class Game implements Formattable, Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countingDown = true;
|
||||||
StartTimer.createTimer(this, event.delay);
|
StartTimer.createTimer(this, event.delay);
|
||||||
event.setHandled();
|
event.setHandled();
|
||||||
}
|
}
|
||||||
@@ -429,6 +455,7 @@ public class Game implements Formattable, Listener {
|
|||||||
if (event.game != this) {
|
if (event.game != this) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
countingDown = false;
|
||||||
running = true;
|
running = true;
|
||||||
removeCages();
|
removeCages();
|
||||||
GameProtection.protect(this, getBox());
|
GameProtection.protect(this, getBox());
|
||||||
@@ -466,6 +493,7 @@ public class Game implements Formattable, Listener {
|
|||||||
if (event.game != this) {
|
if (event.game != this) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
countingDown = false;
|
||||||
if (!running) {
|
if (!running) {
|
||||||
event.cancelFor(Text.STOP_NOT_STARTED.format(new Context(false).plus("game", this)));
|
event.cancelFor(Text.STOP_NOT_STARTED.format(new Context(false).plus("game", this)));
|
||||||
}
|
}
|
||||||
@@ -476,6 +504,7 @@ public class Game implements Formattable, Listener {
|
|||||||
if (event.game != this) {
|
if (event.game != this) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
countingDown = false;
|
||||||
if (running) {
|
if (running) {
|
||||||
running = false;
|
running = false;
|
||||||
BmGameBuildIntent.build(this);
|
BmGameBuildIntent.build(this);
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ public class GamePlayer implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
@EventHandler(ignoreCancelled = false, priority = EventPriority.LOW)
|
||||||
public void onPlayerBreakBlockWithWrongTool(PlayerInteractEvent event) {
|
public void onPlayerBreakBlockWithWrongTool(PlayerInteractEvent event) {
|
||||||
if (event.getPlayer() != player) {
|
if (event.getPlayer() != player) {
|
||||||
return;
|
return;
|
||||||
@@ -290,6 +290,12 @@ public class GamePlayer implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldBypassSpawnProtection(event.getClickedBlock() != null ? event.getClickedBlock().getLocation() : null)) {
|
||||||
|
event.setCancelled(false);
|
||||||
|
event.setUseInteractedBlock(Event.Result.ALLOW);
|
||||||
|
event.setUseItemInHand(Event.Result.ALLOW);
|
||||||
|
}
|
||||||
|
|
||||||
String key = event.getClickedBlock() != null
|
String key = event.getClickedBlock() != null
|
||||||
? event.getClickedBlock().getBlockData().getMaterial().getKey().toString()
|
? event.getClickedBlock().getBlockData().getMaterial().getKey().toString()
|
||||||
: null;
|
: null;
|
||||||
@@ -310,12 +316,17 @@ public class GamePlayer implements Listener {
|
|||||||
event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, 20, 1));
|
event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, 20, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
@EventHandler(ignoreCancelled = false, priority = EventPriority.LOW)
|
||||||
public void onPlayerPlaceBlock(BlockPlaceEvent event) {
|
public void onPlayerPlaceBlock(BlockPlaceEvent event) {
|
||||||
if (event.getPlayer() != player) {
|
if (event.getPlayer() != player) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldBypassSpawnProtection(event.getBlockPlaced().getLocation())) {
|
||||||
|
event.setCancelled(false);
|
||||||
|
event.setBuild(true);
|
||||||
|
}
|
||||||
|
|
||||||
String key = event.getBlockAgainst().getBlockData().getMaterial().getKey().toString();
|
String key = event.getBlockAgainst().getBlockData().getMaterial().getKey().toString();
|
||||||
var nbt = BukkitAdapter.adapt(event.getItemInHand()).getNbtData();
|
var nbt = BukkitAdapter.adapt(event.getItemInHand()).getNbtData();
|
||||||
var list = nbt != null ? nbt.getList("CanPlaceOn", StringTag.class) : null;
|
var list = nbt != null ? nbt.getList("CanPlaceOn", StringTag.class) : null;
|
||||||
@@ -327,11 +338,17 @@ public class GamePlayer implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
@EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST)
|
||||||
public void onPlayerPlaceTNT(BlockPlaceEvent event) {
|
public void onPlayerPlaceTNT(BlockPlaceEvent event) {
|
||||||
if (event.getPlayer() != player) {
|
if (event.getPlayer() != player) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldBypassSpawnProtection(event.getBlockPlaced().getLocation())) {
|
||||||
|
event.setCancelled(false);
|
||||||
|
event.setBuild(true);
|
||||||
|
}
|
||||||
|
|
||||||
var block = event.getBlock();
|
var block = event.getBlock();
|
||||||
if (block.getType() == game.getSettings().getBombItem()) {
|
if (block.getType() == game.getSettings().getBombItem()) {
|
||||||
if (!Bomb.spawnBomb(game, player, block)) {
|
if (!Bomb.spawnBomb(game, player, block)) {
|
||||||
@@ -340,6 +357,10 @@ public class GamePlayer implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldBypassSpawnProtection(Location location) {
|
||||||
|
return game.isRunning() && game.contains(location);
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
||||||
public void onPlayerMoved(PlayerMoveEvent event) {
|
public void onPlayerMoved(PlayerMoveEvent event) {
|
||||||
if (event.getPlayer() != player) {
|
if (event.getPlayer() != player) {
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
|
|||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||||
import io.github.mviper.bomberman.Bomberman;
|
import io.github.mviper.bomberman.Bomberman;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -96,31 +99,100 @@ public class GameSave {
|
|||||||
try {
|
try {
|
||||||
loadGame(file);
|
loadGame(file);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (e instanceof IOException) {
|
||||||
|
PLUGIN.getLogger().warning("Skipping save '" + file + "': " + e.getMessage());
|
||||||
|
} else {
|
||||||
PLUGIN.getLogger().log(Level.WARNING, "Exception occurred while loading: " + file, e);
|
PLUGIN.getLogger().log(Level.WARNING, "Exception occurred while loading: " + file, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
PLUGIN.getLogger().log(Level.WARNING, "Exception occurred while listing saves", e);
|
PLUGIN.getLogger().log(Level.WARNING, "Exception occurred while listing saves", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameSave loadSave(Path zipFile) throws IOException {
|
public static GameSave loadSave(Path zipFile) throws IOException {
|
||||||
PLUGIN.getLogger().info("Reading " + zipFile);
|
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(zipFile, (ClassLoader) null)) {
|
try (FileSystem fs = FileSystems.newFileSystem(zipFile, (ClassLoader) null)) {
|
||||||
Path configPath = fs.getPath("config.yml");
|
Path configPath = fs.getPath("config.yml");
|
||||||
try (var reader = Files.newBufferedReader(configPath)) {
|
try (var reader = Files.newBufferedReader(configPath)) {
|
||||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(reader);
|
Object parsed = new Yaml().load(reader);
|
||||||
String name = config.getString("name");
|
if (!(parsed instanceof Map<?, ?> config)) {
|
||||||
Location origin = config.getSerializable("origin", Location.class);
|
throw new IOException("Cannot read 'config.yml' in '" + zipFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = asString(config.get("name"));
|
||||||
|
Location origin = parseLocation(config.get("origin"), zipFile);
|
||||||
if (name == null || origin == null) {
|
if (name == null || origin == null) {
|
||||||
throw new IOException("Cannot read 'config.yml' in '" + zipFile + "'");
|
throw new IOException("Cannot read 'config.yml' in '" + zipFile + "'");
|
||||||
}
|
}
|
||||||
PLUGIN.getLogger().info(" Data read");
|
|
||||||
return new GameSave(name, origin, zipFile);
|
return new GameSave(name, origin, zipFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Location parseLocation(Object source, Path zipFile) throws IOException {
|
||||||
|
if (source instanceof Location location) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
if (!(source instanceof Map<?, ?> map)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String worldName = asString(map.get("world"));
|
||||||
|
if (worldName == null) {
|
||||||
|
throw new IOException("Cannot read world from 'config.yml' in '" + zipFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
World world = Bukkit.getWorld(worldName);
|
||||||
|
if (world == null) {
|
||||||
|
throw new IOException("Unknown world '" + worldName + "' in '" + zipFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Double x = asDouble(map.get("x"));
|
||||||
|
Double y = asDouble(map.get("y"));
|
||||||
|
Double z = asDouble(map.get("z"));
|
||||||
|
if (x == null || y == null || z == null) {
|
||||||
|
throw new IOException("Cannot read coordinates from 'config.yml' in '" + zipFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Float yaw = asFloat(map.get("yaw"));
|
||||||
|
Float pitch = asFloat(map.get("pitch"));
|
||||||
|
return new Location(world, x, y, z, yaw != null ? yaw : 0.0f, pitch != null ? pitch : 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String asString(Object value) {
|
||||||
|
return value instanceof String text ? text : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Double asDouble(Object value) {
|
||||||
|
if (value instanceof Number number) {
|
||||||
|
return number.doubleValue();
|
||||||
|
}
|
||||||
|
if (value instanceof String text) {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(text);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Float asFloat(Object value) {
|
||||||
|
if (value instanceof Number number) {
|
||||||
|
return number.floatValue();
|
||||||
|
}
|
||||||
|
if (value instanceof String text) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(text);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Game loadGame(Path zipFile) throws IOException {
|
public static Game loadGame(Path zipFile) throws IOException {
|
||||||
return new Game(loadSave(zipFile));
|
return new Game(loadSave(zipFile));
|
||||||
}
|
}
|
||||||
@@ -129,8 +201,6 @@ public class GameSave {
|
|||||||
try (DirectoryStream<Path> files = Files.newDirectoryStream(PLUGIN.gameSaves(), "*.yml")) {
|
try (DirectoryStream<Path> files = Files.newDirectoryStream(PLUGIN.gameSaves(), "*.yml")) {
|
||||||
for (Path file : files) {
|
for (Path file : files) {
|
||||||
try {
|
try {
|
||||||
PLUGIN.getLogger().info("Updating old save: " + file);
|
|
||||||
|
|
||||||
YamlConfiguration config;
|
YamlConfiguration config;
|
||||||
try (var reader = Files.newBufferedReader(file)) {
|
try (var reader = Files.newBufferedReader(file)) {
|
||||||
config = YamlConfiguration.loadConfiguration(reader);
|
config = YamlConfiguration.loadConfiguration(reader);
|
||||||
@@ -153,7 +223,6 @@ public class GameSave {
|
|||||||
settings = builder.build();
|
settings = builder.build();
|
||||||
|
|
||||||
if (name == null || schema == null || origin == null) {
|
if (name == null || schema == null || origin == null) {
|
||||||
PLUGIN.getLogger().info(" Skipping update as file missing data");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,14 +259,12 @@ public class GameSave {
|
|||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN.getLogger().info("Reading schematic data: " + zipPath.getFileName());
|
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(zipPath, (ClassLoader) null)) {
|
try (FileSystem fs = FileSystems.newFileSystem(zipPath, (ClassLoader) null)) {
|
||||||
Path arenaPath = fs.getPath("arena.schem");
|
Path arenaPath = fs.getPath("arena.schem");
|
||||||
Clipboard schematic;
|
Clipboard schematic;
|
||||||
try (var reader = Files.newInputStream(arenaPath)) {
|
try (var reader = Files.newInputStream(arenaPath)) {
|
||||||
schematic = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getReader(reader).read();
|
schematic = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getReader(reader).read();
|
||||||
}
|
}
|
||||||
PLUGIN.getLogger().info("Data read");
|
|
||||||
schematicCache = new WeakReference<>(schematic);
|
schematicCache = new WeakReference<>(schematic);
|
||||||
return schematic;
|
return schematic;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -213,7 +280,6 @@ public class GameSave {
|
|||||||
return settingsCache;
|
return settingsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN.getLogger().info("Reading game settings: " + zipPath.getFileName());
|
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(zipPath, (ClassLoader) null)) {
|
try (FileSystem fs = FileSystems.newFileSystem(zipPath, (ClassLoader) null)) {
|
||||||
Path settingsPath = fs.getPath("settings.yml");
|
Path settingsPath = fs.getPath("settings.yml");
|
||||||
try (var reader = Files.newBufferedReader(settingsPath)) {
|
try (var reader = Files.newBufferedReader(settingsPath)) {
|
||||||
@@ -223,7 +289,6 @@ public class GameSave {
|
|||||||
settings = new GameSettingsBuilder().build();
|
settings = new GameSettingsBuilder().build();
|
||||||
}
|
}
|
||||||
settingsCache = settings;
|
settingsCache = settings;
|
||||||
PLUGIN.getLogger().info("Data read");
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package io.github.mviper.bomberman.game;
|
||||||
|
|
||||||
|
import io.github.mviper.bomberman.Bomberman;
|
||||||
|
import io.github.mviper.bomberman.commands.Permissions;
|
||||||
|
import io.github.mviper.bomberman.events.BmGameLookupIntent;
|
||||||
|
import io.github.mviper.bomberman.events.BmPlayerJoinGameIntent;
|
||||||
|
import io.github.mviper.bomberman.messaging.Context;
|
||||||
|
import io.github.mviper.bomberman.messaging.Message;
|
||||||
|
import io.github.mviper.bomberman.messaging.Text;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.block.Sign;
|
||||||
|
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.block.SignChangeEvent;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
|
|
||||||
|
public class JoinSignListener implements Listener {
|
||||||
|
private static final Bomberman PLUGIN = Bomberman.instance;
|
||||||
|
private static final long REFRESH_INTERVAL_TICKS = 40L;
|
||||||
|
private static final String HEADER = "[Bomberman]";
|
||||||
|
private static final String SHORT_HEADER = "[bm]";
|
||||||
|
private static final String JOIN_ACTION = "join";
|
||||||
|
|
||||||
|
public JoinSignListener() {
|
||||||
|
Bukkit.getScheduler().runTaskTimer(PLUGIN, this::refreshLoadedSigns, REFRESH_INTERVAL_TICKS, REFRESH_INTERVAL_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL)
|
||||||
|
public void onSignCreate(SignChangeEvent event) {
|
||||||
|
if (!isBombermanHeader(event.getLine(0))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Permissions.CONFIGURE.isAllowedBy(event.getPlayer())) {
|
||||||
|
Text.DENY_PERMISSION.format(new Context(false)).sendTo(event.getPlayer());
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String gameName = extractGameName(event.getLine(1), event.getLine(2));
|
||||||
|
if (gameName.isEmpty()) {
|
||||||
|
Message.error("Join-Schild ungültig. Verwende Zeile 2 oder 3 für den Spielnamen.").sendTo(event.getPlayer());
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BmGameLookupIntent.find(gameName) == null) {
|
||||||
|
Message.error("Spiel '" + gameName + "' wurde nicht gefunden.").sendTo(event.getPlayer());
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
applySignFormat(event, gameName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL)
|
||||||
|
public void onSignUse(PlayerInteractEvent event) {
|
||||||
|
if (event.getHand() != EquipmentSlot.HAND || event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(event.getClickedBlock().getState() instanceof Sign sign)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isBombermanHeader(sign.getLine(0))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
if (!Permissions.JOIN.isAllowedBy(player)) {
|
||||||
|
Text.DENY_PERMISSION.format(new Context(false)).sendTo(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String gameName = extractGameName(sign.getLine(1), sign.getLine(2));
|
||||||
|
if (gameName.isEmpty()) {
|
||||||
|
Message.error("Dieses Join-Schild ist ungültig.").sendTo(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game game = BmGameLookupIntent.find(gameName);
|
||||||
|
if (game == null) {
|
||||||
|
Message.error("Spiel '" + gameName + "' wurde nicht gefunden.").sendTo(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinEvent = BmPlayerJoinGameIntent.join(game, player);
|
||||||
|
if (joinEvent.isCancelled()) {
|
||||||
|
if (joinEvent.cancelledReason() != null) {
|
||||||
|
joinEvent.cancelledReason().sendTo(player);
|
||||||
|
} else {
|
||||||
|
Text.COMMAND_CANCELLED.format(new Context(false)
|
||||||
|
.plus("game", game)
|
||||||
|
.plus("player", player))
|
||||||
|
.sendTo(player);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text.JOIN_SUCCESS.format(new Context(false)
|
||||||
|
.plus("game", game)
|
||||||
|
.plus("player", player))
|
||||||
|
.sendTo(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshLoadedSigns() {
|
||||||
|
for (var world : Bukkit.getWorlds()) {
|
||||||
|
for (Chunk chunk : world.getLoadedChunks()) {
|
||||||
|
for (var state : chunk.getTileEntities()) {
|
||||||
|
if (state instanceof Sign sign && isBombermanHeader(sign.getLine(0))) {
|
||||||
|
refreshSign(sign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshSign(Sign sign) {
|
||||||
|
String gameName = extractGameName(sign.getLine(1), sign.getLine(2));
|
||||||
|
if (gameName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game game = BmGameLookupIntent.find(gameName);
|
||||||
|
if (game == null) {
|
||||||
|
updateSign(sign,
|
||||||
|
ChatColor.DARK_BLUE + HEADER,
|
||||||
|
ChatColor.GOLD + gameName,
|
||||||
|
ChatColor.DARK_RED + "Offline",
|
||||||
|
ChatColor.GRAY + "Spiel fehlt");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSign(sign,
|
||||||
|
ChatColor.DARK_BLUE + HEADER,
|
||||||
|
ChatColor.GOLD + game.getName(),
|
||||||
|
statusLine(game),
|
||||||
|
playersLine(game));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySignFormat(SignChangeEvent event, String gameName) {
|
||||||
|
Game game = BmGameLookupIntent.find(gameName);
|
||||||
|
if (game == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setLine(0, ChatColor.DARK_BLUE + HEADER);
|
||||||
|
event.setLine(1, ChatColor.GOLD + game.getName());
|
||||||
|
event.setLine(2, statusLine(game));
|
||||||
|
event.setLine(3, playersLine(game));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSign(Sign sign, String line0, String line1, String line2, String line3) {
|
||||||
|
if (sameLine(sign.getLine(0), line0)
|
||||||
|
&& sameLine(sign.getLine(1), line1)
|
||||||
|
&& sameLine(sign.getLine(2), line2)
|
||||||
|
&& sameLine(sign.getLine(3), line3)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sign.setLine(0, line0);
|
||||||
|
sign.setLine(1, line1);
|
||||||
|
sign.setLine(2, line2);
|
||||||
|
sign.setLine(3, line3);
|
||||||
|
sign.update(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String statusLine(Game game) {
|
||||||
|
if (game.isRunning()) {
|
||||||
|
return ChatColor.RED + "Läuft";
|
||||||
|
}
|
||||||
|
if (game.isCountingDown()) {
|
||||||
|
return ChatColor.YELLOW + "Startet";
|
||||||
|
}
|
||||||
|
return ChatColor.GREEN + "Wartet";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String playersLine(Game game) {
|
||||||
|
return ChatColor.GRAY + Integer.toString(game.getPlayerCount()) + "/" + game.getMaxPlayers() + " Spieler";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sameLine(String current, String expected) {
|
||||||
|
return normalize(current).equalsIgnoreCase(normalize(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBombermanHeader(String value) {
|
||||||
|
String normalized = normalize(value);
|
||||||
|
return HEADER.equalsIgnoreCase(normalized) || SHORT_HEADER.equalsIgnoreCase(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractGameName(String secondLine, String thirdLine) {
|
||||||
|
String lineTwo = normalize(secondLine);
|
||||||
|
String lineThree = normalize(thirdLine);
|
||||||
|
if (JOIN_ACTION.equalsIgnoreCase(lineTwo)) {
|
||||||
|
return lineThree;
|
||||||
|
}
|
||||||
|
return lineTwo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalize(String value) {
|
||||||
|
return value == null ? "" : ChatColor.stripColor(value).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
# BOMBERMAN CONFIG #
|
# BOMBERMAN CONFIG #
|
||||||
# #
|
# #
|
||||||
# Refer to the Bomberman Wiki for help on configuration: #
|
# Refer to the Bomberman Wiki for help on configuration: #
|
||||||
# https://github.com/mviper/bomberman/wiki #
|
# https://git.viper.ipv64.net/M_Viper/Bombermann #
|
||||||
# #
|
# #
|
||||||
# There is nothing to configure in this file. All configuration #
|
# There is nothing to configure in this file. All configuration #
|
||||||
# is done on a per-game basis. #
|
# is done on a per-game basis. #
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# You can overwrite anything by setting the value in messages.yml #
|
# You can overwrite anything by setting the value in messages.yml #
|
||||||
# #
|
# #
|
||||||
# Refer to the Bomberman wiki for help on configuring messages: #
|
# Refer to the Bomberman wiki for help on configuring messages: #
|
||||||
# https://github.com/mviper/bomberman/wiki/Localisation #
|
# https://git.viper.ipv64.net/M_Viper/Bombermann #
|
||||||
# #
|
# #
|
||||||
# This file is automatically generated and will be reset on next #
|
# This file is automatically generated and will be reset on next #
|
||||||
# server reload. #
|
# server reload. #
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ api-version: 1.21
|
|||||||
load: POSTWORLD
|
load: POSTWORLD
|
||||||
|
|
||||||
author: M_Viper
|
author: M_Viper
|
||||||
website: https://github.com/mviper/bomberman
|
website: https://git.viper.ipv64.net/M_Viper/Bombermann
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
bomberman:
|
bomberman:
|
||||||
|
|||||||
Reference in New Issue
Block a user