From 5b75b83c5b6756b9a370adf80fa60cbe11511cea Mon Sep 17 00:00:00 2001 From: Tobias Ottenweller Date: Sat, 8 Jun 2013 16:18:09 +0200 Subject: [PATCH] Implemented a world generator. The generator is currently not working! Unsolved problem: How to force bukkit to populate a chunk. --- .../craftinc/borderprotection/Messages.java | 3 +- .../de/craftinc/borderprotection/Plugin.java | 8 +- .../borderprotection/borders/Border.java | 6 + .../borderprotection/borders/CircBorder.java | 9 + .../borderprotection/borders/RectBorder.java | 6 + .../commands/CancelGenerateCommand.java | 63 +++++ .../commands/CommandSwitch.java | 9 +- .../commands/GenerateCommand.java | 26 +- .../commands/HelpCommand.java | 1 - .../commands/OnOffCommand.java | 1 - .../borderprotection/commands/SubCommand.java | 9 +- .../events/ChunkLoadingListener.java | 44 ++++ .../events/PlayerLoginListener.java | 4 + .../events/PlayerQuitListener.java | 37 +++ .../borderprotection/util/ChunkGenerator.java | 226 ++++++++++++++++++ 15 files changed, 430 insertions(+), 22 deletions(-) create mode 100644 src/main/java/de/craftinc/borderprotection/commands/CancelGenerateCommand.java create mode 100644 src/main/java/de/craftinc/borderprotection/events/ChunkLoadingListener.java create mode 100644 src/main/java/de/craftinc/borderprotection/events/PlayerQuitListener.java create mode 100644 src/main/java/de/craftinc/borderprotection/util/ChunkGenerator.java diff --git a/src/main/java/de/craftinc/borderprotection/Messages.java b/src/main/java/de/craftinc/borderprotection/Messages.java index bbc369b..e98eca5 100644 --- a/src/main/java/de/craftinc/borderprotection/Messages.java +++ b/src/main/java/de/craftinc/borderprotection/Messages.java @@ -84,7 +84,8 @@ public class Messages ChatColor.WHITE + "Commands are always related to the current world." + NEWLINE + makeCmd("help", "shows this help") + makeCmd("get | info", "Shows information about the border.") + - makeCmd("generate", "Generate all chunks inside the border", "on | off") + + makeCmd("generate", "Generate all not existing chunks inside the border.") + + makeCmd("cancelgenerate", "Cancels the generation of chunks.") + makeCmd("on | off", "Enables/disables the border.") + makeCmd("set", "Square border with distance (d) from 0,0.", "r", "") + makeCmd("set", "Rectangle defined by two points. Point=x,z.", "r", "", "") + diff --git a/src/main/java/de/craftinc/borderprotection/Plugin.java b/src/main/java/de/craftinc/borderprotection/Plugin.java index 5c7f201..b0df6ef 100644 --- a/src/main/java/de/craftinc/borderprotection/Plugin.java +++ b/src/main/java/de/craftinc/borderprotection/Plugin.java @@ -19,9 +19,7 @@ package de.craftinc.borderprotection; import de.craftinc.borderprotection.borders.CircBorder; import de.craftinc.borderprotection.borders.RectBorder; import de.craftinc.borderprotection.commands.CommandSwitch; -import de.craftinc.borderprotection.events.PlayerLoginListener; -import de.craftinc.borderprotection.events.PlayerMoveListener; -import de.craftinc.borderprotection.events.PlayerTeleportListener; +import de.craftinc.borderprotection.events.*; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -51,6 +49,8 @@ public class Plugin extends JavaPlugin PlayerMoveListener playerMoveListener = new PlayerMoveListener(); PlayerTeleportListener playerTeleportListener = new PlayerTeleportListener(); PlayerLoginListener playerLoginListener = new PlayerLoginListener(); + ChunkLoadingListener chunkLoadingListener = new ChunkLoadingListener(); + PlayerQuitListener playerQuitListener = new PlayerQuitListener(); // commands CommandSwitch commandExecutor = new CommandSwitch(); @@ -61,5 +61,7 @@ public class Plugin extends JavaPlugin pm.registerEvents(playerMoveListener, this); pm.registerEvents(playerTeleportListener, this); pm.registerEvents(playerLoginListener, this); + pm.registerEvents(chunkLoadingListener, this); + pm.registerEvents(playerQuitListener, this); } } diff --git a/src/main/java/de/craftinc/borderprotection/borders/Border.java b/src/main/java/de/craftinc/borderprotection/borders/Border.java index 8a72be1..1ab9ea8 100644 --- a/src/main/java/de/craftinc/borderprotection/borders/Border.java +++ b/src/main/java/de/craftinc/borderprotection/borders/Border.java @@ -73,6 +73,12 @@ public abstract class Border */ public abstract Location checkBorder( Location l ); + /** + * Returns an array of two Location objects defining a rectangle bigger or at size of the border. There are no + * locations outside the rectangle which are inside the border. + */ + public abstract Location[] getSurroundingRect(); + public Boolean isActive() { return isActive; diff --git a/src/main/java/de/craftinc/borderprotection/borders/CircBorder.java b/src/main/java/de/craftinc/borderprotection/borders/CircBorder.java index 2276a21..ee4b699 100644 --- a/src/main/java/de/craftinc/borderprotection/borders/CircBorder.java +++ b/src/main/java/de/craftinc/borderprotection/borders/CircBorder.java @@ -120,4 +120,13 @@ public class CircBorder extends Border implements ConfigurationSerializable return newLocation; } + + @Override + public Location[] getSurroundingRect() + { + Location l1 = new Location(center.getWorld(), center.getX()+radius, center.getY(), center.getX()+radius); + Location l2 = new Location(center.getWorld(), center.getX()-radius, center.getY(), center.getX()-radius); + + return new Location[]{ l1, l2 }; + } } \ No newline at end of file diff --git a/src/main/java/de/craftinc/borderprotection/borders/RectBorder.java b/src/main/java/de/craftinc/borderprotection/borders/RectBorder.java index 7b26012..2091f71 100644 --- a/src/main/java/de/craftinc/borderprotection/borders/RectBorder.java +++ b/src/main/java/de/craftinc/borderprotection/borders/RectBorder.java @@ -180,4 +180,10 @@ public class RectBorder extends Border implements ConfigurationSerializable } } } + + @Override + public Location[] getSurroundingRect() + { + return new Location[]{ rectPoint1, rectPoint2 }; + } } diff --git a/src/main/java/de/craftinc/borderprotection/commands/CancelGenerateCommand.java b/src/main/java/de/craftinc/borderprotection/commands/CancelGenerateCommand.java new file mode 100644 index 0000000..1fc1d7f --- /dev/null +++ b/src/main/java/de/craftinc/borderprotection/commands/CancelGenerateCommand.java @@ -0,0 +1,63 @@ +/* Craft Inc. BorderProtection + Copyright (C) 2013 Paul Schulze + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package de.craftinc.borderprotection.commands; + + +import de.craftinc.borderprotection.Messages; +import de.craftinc.borderprotection.util.ChunkGenerator; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class CancelGenerateCommand implements SubCommand +{ + @Override + public boolean execute(CommandSender sender, String[] parameters) + { + if ( !sender.hasPermission("craftinc.borderprotection.set") ) // TODO: a new/different permission? + { + sender.sendMessage(Messages.noPermissionSet); + return false; + } + + World world = ( (Player) sender ).getWorld(); + + if (!ChunkGenerator.isGenerating(world)) + { + sender.sendMessage("nothing to cancel"); // TODO: put better message into Message class + } + else + { + ChunkGenerator.cancelRender(world); + sender.sendMessage("generation canceled"); // TODO: put better message into Message class + } + + return true; + } + + @Override + public List commandNames() + { + ArrayList names = new ArrayList(); + names.add("cancelgenerate"); + + return names; + } +} \ No newline at end of file diff --git a/src/main/java/de/craftinc/borderprotection/commands/CommandSwitch.java b/src/main/java/de/craftinc/borderprotection/commands/CommandSwitch.java index f110817..2fe8b66 100644 --- a/src/main/java/de/craftinc/borderprotection/commands/CommandSwitch.java +++ b/src/main/java/de/craftinc/borderprotection/commands/CommandSwitch.java @@ -17,19 +17,11 @@ package de.craftinc.borderprotection.commands; import de.craftinc.borderprotection.Messages; -import de.craftinc.borderprotection.borders.Border; -import de.craftinc.borderprotection.borders.CircBorder; -import de.craftinc.borderprotection.borders.RectBorder; -import de.craftinc.borderprotection.util.UpdateHelper; -import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -39,6 +31,7 @@ public class CommandSwitch implements CommandExecutor public CommandSwitch() { + registerCommand(new CancelGenerateCommand()); registerCommand(new CheckVersionCommand()); registerCommand(new GenerateCommand()); registerCommand(new GetCommand()); diff --git a/src/main/java/de/craftinc/borderprotection/commands/GenerateCommand.java b/src/main/java/de/craftinc/borderprotection/commands/GenerateCommand.java index 68e9421..f61eb2f 100644 --- a/src/main/java/de/craftinc/borderprotection/commands/GenerateCommand.java +++ b/src/main/java/de/craftinc/borderprotection/commands/GenerateCommand.java @@ -17,12 +17,15 @@ package de.craftinc.borderprotection.commands; import de.craftinc.borderprotection.Messages; +import de.craftinc.borderprotection.util.ChunkGenerator; +import org.bukkit.World; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; -public class GenerateCommand implements SubCommand +public class GenerateCommand implements SubCommand { @Override public boolean execute(CommandSender sender, String[] parameters) @@ -33,9 +36,26 @@ public class GenerateCommand implements SubCommand return false; } - // TODO: implement me! + World world = ( (Player) sender ).getWorld(); - return false; + if (ChunkGenerator.isGenerating(world)) + { + sender.sendMessage("already generating! will resume on logout"); // TODO: put better message into Message class + } + else + { + if (ChunkGenerator.generate(world)) + { + sender.sendMessage("world marked for generation! will start on logout"); // TODO: put better message into Message class + } + else + { + sender.sendMessage("could not start generation. is there a border?"); // TODO: put better message into Message class + } + + } + + return true; } @Override diff --git a/src/main/java/de/craftinc/borderprotection/commands/HelpCommand.java b/src/main/java/de/craftinc/borderprotection/commands/HelpCommand.java index 448fcd5..8c29cb6 100644 --- a/src/main/java/de/craftinc/borderprotection/commands/HelpCommand.java +++ b/src/main/java/de/craftinc/borderprotection/commands/HelpCommand.java @@ -17,7 +17,6 @@ package de.craftinc.borderprotection.commands; import de.craftinc.borderprotection.Messages; -import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import java.util.ArrayList; diff --git a/src/main/java/de/craftinc/borderprotection/commands/OnOffCommand.java b/src/main/java/de/craftinc/borderprotection/commands/OnOffCommand.java index 0ec39f2..1e2d244 100644 --- a/src/main/java/de/craftinc/borderprotection/commands/OnOffCommand.java +++ b/src/main/java/de/craftinc/borderprotection/commands/OnOffCommand.java @@ -20,7 +20,6 @@ package de.craftinc.borderprotection.commands; import de.craftinc.borderprotection.Messages; import de.craftinc.borderprotection.borders.Border; import org.bukkit.World; -import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; diff --git a/src/main/java/de/craftinc/borderprotection/commands/SubCommand.java b/src/main/java/de/craftinc/borderprotection/commands/SubCommand.java index 0a62364..acaa786 100644 --- a/src/main/java/de/craftinc/borderprotection/commands/SubCommand.java +++ b/src/main/java/de/craftinc/borderprotection/commands/SubCommand.java @@ -26,15 +26,14 @@ public interface SubCommand { /** * - * @param sender will contain the command name at index 0. - * @param parameters - * @return + * @param sender The sender (player) who executed the command. + * @param parameters All parameters for executing this subcommand. Includes the subcommand name at index 0. + * @return Returns an boolean indicating if the subcommand got could be executed. */ public boolean execute(CommandSender sender, String[] parameters); /** - * - * @return a list of names of the command. All strings should be lowercase! + * Returns a list of names of the command. All strings should be lowercase! */ public List commandNames(); } diff --git a/src/main/java/de/craftinc/borderprotection/events/ChunkLoadingListener.java b/src/main/java/de/craftinc/borderprotection/events/ChunkLoadingListener.java new file mode 100644 index 0000000..8f86f2e --- /dev/null +++ b/src/main/java/de/craftinc/borderprotection/events/ChunkLoadingListener.java @@ -0,0 +1,44 @@ +/* Craft Inc. BorderProtection + Copyright (C) 2013 Paul Schulze, Tobias Ottenweller + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package de.craftinc.borderprotection.events; + +import de.craftinc.borderprotection.Plugin; +import de.craftinc.borderprotection.util.ChunkGenerator; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkPopulateEvent; + +public class ChunkLoadingListener implements Listener +{ + @SuppressWarnings("unused") + @EventHandler(priority = EventPriority.NORMAL) + public void onChunkPopulate( ChunkPopulateEvent e ) + { + ChunkGenerator.handleChunkLoad(e.getChunk(), true); + } + + + @SuppressWarnings("unused") + @EventHandler(priority = EventPriority.NORMAL) + public void onChunkLoad( ChunkLoadEvent e ) + { + boolean populated = !e.isNewChunk(); + ChunkGenerator.handleChunkLoad(e.getChunk(), populated); + } +} diff --git a/src/main/java/de/craftinc/borderprotection/events/PlayerLoginListener.java b/src/main/java/de/craftinc/borderprotection/events/PlayerLoginListener.java index 26b016e..8089da0 100644 --- a/src/main/java/de/craftinc/borderprotection/events/PlayerLoginListener.java +++ b/src/main/java/de/craftinc/borderprotection/events/PlayerLoginListener.java @@ -18,6 +18,7 @@ package de.craftinc.borderprotection.events; import de.craftinc.borderprotection.Messages; import de.craftinc.borderprotection.Plugin; +import de.craftinc.borderprotection.util.ChunkGenerator; import de.craftinc.borderprotection.util.UpdateHelper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -50,5 +51,8 @@ public class PlayerLoginListener implements Listener }, 20L); } } + + System.out.println("pausing generation"); + ChunkGenerator.pause(); } } diff --git a/src/main/java/de/craftinc/borderprotection/events/PlayerQuitListener.java b/src/main/java/de/craftinc/borderprotection/events/PlayerQuitListener.java new file mode 100644 index 0000000..dd5ed8c --- /dev/null +++ b/src/main/java/de/craftinc/borderprotection/events/PlayerQuitListener.java @@ -0,0 +1,37 @@ +/* Craft Inc. BorderProtection + Copyright (C) 2013 Paul Schulze, Tobias Ottenweller + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package de.craftinc.borderprotection.events; + +import de.craftinc.borderprotection.Plugin; +import de.craftinc.borderprotection.util.ChunkGenerator; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerQuitListener implements Listener +{ + @SuppressWarnings("unused") + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerQuit( PlayerQuitEvent e ) + { + if (Plugin.instance.getServer().getOnlinePlayers().length == 1) + { + ChunkGenerator.resume(); + } + } +} diff --git a/src/main/java/de/craftinc/borderprotection/util/ChunkGenerator.java b/src/main/java/de/craftinc/borderprotection/util/ChunkGenerator.java new file mode 100644 index 0000000..8f993e5 --- /dev/null +++ b/src/main/java/de/craftinc/borderprotection/util/ChunkGenerator.java @@ -0,0 +1,226 @@ +/* Craft Inc. BorderProtection + Copyright (C) 2013 Paul Schulze, Tobias Ottenweller + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package de.craftinc.borderprotection.util; + +import de.craftinc.borderprotection.Plugin; +import de.craftinc.borderprotection.borders.Border; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class ChunkGenerator +{ + protected static Map chunkGenerationStatus = new HashMap(); + protected static boolean isPaused = true; + protected static ArrayList loadedChunks = new ArrayList(); + + public static void pause() + { + isPaused = true; + } + + public static void resume() + { + if (isPaused) + { + isPaused = false; + + for (World w : chunkGenerationStatus.keySet()) + { + loadNextChunk(w); + } + } + } + + public static void cancelRender(World w) + { + if (w == null) + { + throw new IllegalArgumentException("World 'w' must not be null!"); + } + + chunkGenerationStatus.remove(w); + } + + public static boolean isGenerating(World w) + { + if (w == null) + { + throw new IllegalArgumentException("World 'w' must not be null!"); + } + + return chunkGenerationStatus.containsKey(w); + } + + /** + * Starts the generation of all chunks inside a border. + * @param w The world in which chunks will be generated. Must not be 'null'. An exception will be thrown otherwise! + * @return A boolean indicating if the generation was successfully started. Will return false if no border exists + * for a given world. Will return true if the generation was already running but will not restart the generation. + */ + public static boolean generate(World w) + { + if (w == null) + { + throw new IllegalArgumentException("World 'w' must not be null!"); + } + + Border border = Border.getBorders().get(w); + + if (border == null) + { + return false; + } + + Location[] borderRect = border.getSurroundingRect(); + + int firstChunkX = Math.min(borderRect[0].getBlockX(), borderRect[1].getBlockX()) >> 4; + int firstChunkZ = Math.min(borderRect[0].getBlockZ(), borderRect[1].getBlockZ()) >> 4; + + firstChunkX--; + + chunkGenerationStatus.put(w, new Integer[]{firstChunkX, firstChunkZ}); + // the actual generation will start when resume is called! + + return true; + } + + public static void handleChunkLoad(Chunk c, boolean isPopulated) + { + if (c == null) + { + throw new IllegalArgumentException("Chunk 'c' must not be null!"); + } + + if (isPaused) + { + return; + } + + World w = c.getWorld(); + Integer[] currentGenerationChunk = chunkGenerationStatus.get(w); + + if ((currentGenerationChunk != null) && c.getX() == currentGenerationChunk[0] && c.getZ() == currentGenerationChunk[1]) + { + if (!isPopulated) + { + Plugin.instance.getLogger().info("Trying to get the chunk to get populated"); +// loadSurroundingChunks(c.getX(), c.getZ(), w); + + } + else + { + unloadLoadedChunks(); + loadNextChunk(c.getWorld()); + } + } + } + + protected static void loadSurroundingChunks(int x, int z, World w) + { + int radius = 2; + + for (int i=-radius; i> 4; + int maxChunkX = Math.max(borderRect[0].getBlockX(), borderRect[1].getBlockX()) >> 4; + int maxChunkZ = Math.max(borderRect[0].getBlockZ(), borderRect[1].getBlockZ()) >> 4; + + chunkX++; + + while (!chunkIsInsideBorder(chunkX, chunkZ, w, border) + && chunkZ <= maxChunkZ + /*&& !w.isChunkLoaded(chunkX, chunkZ)*/) + { + chunkX++; + + if (chunkX > maxChunkX) + { + chunkZ++; + chunkX = minChunkX; + } + } + + if (chunkZ <= maxChunkZ) + { + chunkGenerationStatus.put(w, new Integer[]{chunkX, chunkZ}); + Plugin.instance.getLogger().info("Loading/Generating Chunk ( x=" + chunkX + " z=" + chunkZ + " world=" + w.getName() + " )"); +// loadSurroundingChunks(chunkX, chunkZ, w); + w.loadChunk(chunkX, chunkZ, true); + } + else + { + Plugin.instance.getLogger().info("Finished generating Chunks for world " + w.getName()); + chunkGenerationStatus.remove(w); + } + } + + protected static boolean chunkIsInsideBorder(int x, int z, World w, Border b) + { +// Location chunkLocation = new Location(w, (double)(x << 4), (double)100, (double)(z << 4)); +// return b.checkBorder(chunkLocation) == null; + return true; + } +}