| /* Copyright 2015-2020 Rivoreo |
| |
| This Source Code Form is subject to the terms of the Mozilla Public |
| License, v. 2.0. If a copy of the MPL was not distributed with this |
| file, You can obtain one at https://mozilla.org/MPL/2.0/. |
| */ |
| |
| package rivoreo.minecraft.worldmgr; |
| |
| import org.bukkit.plugin.java.JavaPlugin; |
| import org.bukkit.command.Command; |
| import org.bukkit.command.CommandExecutor; |
| import org.bukkit.command.CommandSender; |
| import org.bukkit.event.player.PlayerTeleportEvent; |
| import org.bukkit.entity.Player; |
| import org.bukkit.Location; |
| import org.bukkit.Material; |
| import org.bukkit.Server; |
| import org.bukkit.World; |
| import org.bukkit.WorldCreator; |
| import org.bukkit.WorldType; |
| import java.util.logging.Logger; |
| import java.util.logging.Level; |
| import java.util.BitSet; |
| import java.util.Collection; |
| import java.util.Hashtable; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.InvocationTargetException; |
| import java.io.File; |
| import java.io.IOException; |
| |
| public final class BukkitPlugin extends JavaPlugin implements CommandExecutor { |
| public BukkitPlugin() { |
| bukkit_world_environment_ordinal = World.Environment.values().length; |
| } |
| |
| private static boolean try_minecraftforge = true; |
| private static Class<?> forge_world_manager_class; |
| private static Hashtable<Integer, Class<?>> world_providers; |
| private static Hashtable<Integer, Object> forge_loaded_worlds; |
| private static Class<?> mc_world_provider_class; |
| private static Method mc_world_provider_get_name_method; |
| private static Class<?> mc_world_class; |
| private static Field mc_world_provider_field; |
| private static Method craftbukkit_world_get_handle_method; |
| |
| private static BitSet forge_world_id_map; |
| private static Method forge_world_manager_load_world_method; |
| private static Method forge_register_world_provider_type_method; |
| |
| private static Method mc_world_provider_get_movement_factor_method; |
| private static Method mc_world_provider_register_world_method; |
| private static Method mc_world_provider_set_spawn_point_method; |
| |
| private static Class<?> customworldprovider_class; |
| private static Constructor<?> customworldprovider_constructor; |
| private static Method customworldprovider_set_world_name_method; |
| private static Method customworldprovider_set_average_ground_level_method; |
| private static Method customworldprovider_set_max_height_method; |
| private static Method customworldprovider_set_force_snow_method; |
| private static Method customworldprovider_set_allow_lighting_method; |
| private static Method customworldprovider_set_allow_snow_and_ice_method; |
| |
| private static Constructor<World.Environment> bukkit_world_environment_construtor; |
| private static int bukkit_world_environment_ordinal; |
| private static Field craftbukkit_world_environment_field; |
| |
| private static Field constructor_accessor_field; |
| private static Method acquire_constructor_accessor_method; |
| private static Method constructor_accessor_new_instance_method; |
| |
| private static Field field_modifiers_field; |
| |
| private static boolean try_bukkit_worldborder = true; |
| private static Method bukkit_world_get_world_border_method; |
| private static Class<?> bukkit_worldborder_interface; |
| private static Method bukkit_worldborder_set_center_method; |
| private static Method bukkit_worldborder_set_size_method; |
| |
| private Logger log; |
| private Server server; |
| |
| private static boolean is_minecraftforge_available() { |
| if(!try_minecraftforge) return false; |
| if(forge_world_manager_class == null || world_providers == null || forge_loaded_worlds == null || mc_world_provider_class == null || mc_world_class == null || mc_world_provider_field == null || mc_world_provider_get_name_method == null) try { |
| forge_world_manager_class = Class.forName("net.minecraftforge.common.DimensionManager"); |
| if(world_providers == null) { |
| Field providers_field = forge_world_manager_class.getDeclaredField("providers"); |
| if(providers_field.getType() != Hashtable.class) return false; |
| providers_field.setAccessible(true); |
| world_providers = (Hashtable<Integer, Class<?>>)providers_field.get(null); |
| } |
| if(forge_loaded_worlds == null) { |
| Field worlds_field = forge_world_manager_class.getDeclaredField("worlds"); |
| if(worlds_field.getType() != Hashtable.class) return false; |
| worlds_field.setAccessible(true); |
| forge_loaded_worlds = (Hashtable<Integer, Object>)worlds_field.get(null); |
| } |
| if(mc_world_provider_class == null) { |
| Method get_provider_method = forge_world_manager_class.getDeclaredMethod("getProvider", int.class); |
| mc_world_provider_class = get_provider_method.getReturnType(); |
| } |
| if(mc_world_class == null) { |
| Method get_world_method = forge_world_manager_class.getDeclaredMethod("getWorld", int.class); |
| mc_world_class = get_world_method.getReturnType().getSuperclass(); |
| } |
| if(mc_world_provider_field == null) try { |
| mc_world_provider_field = mc_world_class.getDeclaredField("field_73011_w"); |
| } catch(NoSuchFieldException e) { |
| mc_world_provider_field = mc_world_class.getDeclaredField("provider"); |
| } |
| if(mc_world_provider_get_name_method == null) try { |
| mc_world_provider_get_name_method = mc_world_provider_class.getDeclaredMethod("func_80007_l"); |
| if(mc_world_provider_get_name_method.getReturnType() != String.class) { |
| mc_world_provider_get_name_method = null; |
| throw new NoSuchMethodException("return type mismatch"); |
| } |
| } catch(NoSuchMethodException e) { |
| mc_world_provider_get_name_method = mc_world_provider_class.getDeclaredMethod("getDimensionName"); |
| if(mc_world_provider_get_name_method.getReturnType() != String.class) { |
| mc_world_provider_get_name_method = null; |
| throw new NoSuchMethodException("return type mismatch"); |
| } |
| } |
| } catch(Exception e) { |
| try_minecraftforge = false; |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean is_bukkit_worldborder_interface_available() { |
| if(!try_bukkit_worldborder) return false; |
| if(bukkit_world_get_world_border_method == null || bukkit_worldborder_interface == null || bukkit_worldborder_set_center_method == null || bukkit_worldborder_set_size_method == null) try { |
| bukkit_world_get_world_border_method = World.class.getDeclaredMethod("getWorldBorder"); |
| bukkit_worldborder_interface = Class.forName("org.bukkit.WorldBorder"); |
| if(bukkit_world_get_world_border_method.getReturnType() != bukkit_worldborder_interface) { |
| try_bukkit_worldborder = false; |
| return false; |
| } |
| if(bukkit_worldborder_set_center_method == null) { |
| bukkit_worldborder_set_center_method = bukkit_worldborder_interface.getDeclaredMethod("setCenter", double.class, double.class); |
| } |
| if(bukkit_worldborder_set_size_method == null) { |
| bukkit_worldborder_set_size_method = bukkit_worldborder_interface.getDeclaredMethod("setSize", double.class); |
| } |
| } catch(Exception e) { |
| try_bukkit_worldborder = false; |
| return false; |
| } |
| return true; |
| } |
| |
| // Cauldron only |
| private static Object get_world_provider(World world) throws IllegalAccessException, InvocationTargetException { |
| if(craftbukkit_world_get_handle_method == null) try { |
| craftbukkit_world_get_handle_method = world.getClass().getDeclaredMethod("getHandle"); |
| } catch(NoSuchMethodException e) { |
| e.printStackTrace(); |
| return null; |
| } |
| Object mc_world = craftbukkit_world_get_handle_method.invoke(world); |
| return mc_world_provider_field.get(mc_world); |
| } |
| |
| // Cauldron only |
| private static String get_world_provider_name(World world) { |
| try { |
| Object provider = get_world_provider(world); |
| if(provider == null) return null; |
| return (String)mc_world_provider_get_name_method.invoke(provider); |
| } catch(IllegalAccessException e) { |
| e.printStackTrace(); |
| } catch(InvocationTargetException e) { |
| e.printStackTrace(); |
| } |
| return null; |
| } |
| |
| private World get_world_by_provider_name(String name) { |
| if(!is_minecraftforge_available()) return null; |
| for(World world : server.getWorlds()) { |
| String provider_name = get_world_provider_name(world); |
| if(name.equalsIgnoreCase(provider_name)) return world; |
| if(name.equalsIgnoreCase(provider_name.replace(' ', '_'))) return world; |
| } |
| return null; |
| } |
| |
| private World get_world_by_name_or_uuid(String name) { |
| World world; |
| try { |
| UUID uuid = UUID.fromString(name); |
| world = server.getWorld(uuid); |
| if(world == null) { |
| throw new IllegalArgumentException("world not found"); |
| } |
| } catch(IllegalArgumentException e) { |
| world = server.getWorld(name); |
| if(world == null) world = get_world_by_provider_name(name); |
| } |
| return world; |
| } |
| |
| private void list_loaded_worlds(CommandSender sender) { |
| List<World> worlds = server.getWorlds(); |
| switch(worlds.size()) { |
| case 0: |
| sender.sendMessage("No loaded worlds"); |
| return; |
| case 1: |
| sender.sendMessage("Loaded world:"); |
| break; |
| default: |
| sender.sendMessage("Loaded worlds:"); |
| break; |
| } |
| for(World w : worlds) if(is_minecraftforge_available()) { |
| sender.sendMessage(String.format(" %s '%s' (%s)", |
| get_world_provider_name(w).replace(' ', '_'), w.getName(), w.getUID().toString())); |
| } else { |
| sender.sendMessage(String.format(" %s (%s)", w.getName(), w.getUID().toString())); |
| } |
| } |
| |
| private void list_unloaded_worlds(CommandSender sender) { |
| if(!is_minecraftforge_available()) return; |
| List<String> unloaded_world_names = new ArrayList<String>(); |
| for(Map.Entry<Integer, Class<?>> entry : world_providers.entrySet()) try { |
| Class<?> provider_class = entry.getValue(); |
| if(provider_class == customworldprovider_class) continue; |
| Object provider = provider_class.newInstance(); |
| String name = (String)mc_world_provider_get_name_method.invoke(provider); |
| boolean is_loaded = false; |
| for(Object mc_world : forge_loaded_worlds.values()) { |
| String loaded_name = (String)mc_world_provider_get_name_method.invoke(mc_world_provider_field.get(mc_world)); |
| if(name.equals((String)mc_world_provider_get_name_method.invoke(mc_world_provider_field.get(mc_world)))) { |
| is_loaded = true; |
| break; |
| } |
| } |
| if(!is_loaded) unloaded_world_names.add(name.replace(' ', '_')); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| } |
| switch(unloaded_world_names.size()) { |
| case 0: |
| sender.sendMessage("No unloaded worlds"); |
| break; |
| case 1: |
| sender.sendMessage("Unloaded world:\n " + unloaded_world_names.get(0)); |
| break; |
| default: |
| sender.sendMessage("Unloaded worlds:"); |
| for(String name : unloaded_world_names) sender.sendMessage(" " + name); |
| } |
| } |
| |
| // Cauldron only |
| private World load_world(CommandSender sender, String name) { |
| World world = server.getWorld(name); |
| if(world == null) world = get_world_by_provider_name(name); |
| if(world != null) { |
| sender.sendMessage(String.format("World %s is already loaded", name)); |
| return world; |
| } |
| if(forge_world_manager_load_world_method == null) try { |
| forge_world_manager_load_world_method = forge_world_manager_class.getDeclaredMethod("initDimension", int.class); |
| } catch(NoSuchMethodException e) { |
| e.printStackTrace(); |
| sender.sendMessage(String.format("Failed to load world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| return null; |
| } |
| for(Map.Entry<Integer, Class<?>> entry : world_providers.entrySet()) try { |
| Object provider = entry.getValue().newInstance(); |
| String provider_name = (String)mc_world_provider_get_name_method.invoke(provider); |
| if(name.equalsIgnoreCase(provider_name.replace(' ', '_'))) try { |
| forge_world_manager_load_world_method.invoke(null, entry.getKey()); |
| world = get_world_by_provider_name(provider_name); |
| if(world == null) { |
| sender.sendMessage(String.format("Failed to load world %s for unknown reason", name)); |
| } |
| return world; |
| } catch(Exception e) { |
| e.printStackTrace(); |
| if(e instanceof InvocationTargetException) { |
| Throwable cause = ((InvocationTargetException)e).getCause(); |
| if(cause != null && cause instanceof Exception) e = (Exception)cause; |
| } |
| sender.sendMessage(String.format("Failed to load world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| return null; |
| } |
| } catch(Exception e) { |
| e.printStackTrace(); |
| if(e instanceof InvocationTargetException) { |
| Throwable cause = ((InvocationTargetException)e).getCause(); |
| if(cause != null && cause instanceof Exception) e = (Exception)cause; |
| } |
| sender.sendMessage(e.getMessage()); |
| return null; |
| } |
| sender.sendMessage(String.format("World %s not found", name)); |
| return null; |
| } |
| |
| // Cauldron only |
| private static void load_all_worlds(CommandSender sender) { |
| if(forge_world_manager_load_world_method == null) try { |
| forge_world_manager_load_world_method = forge_world_manager_class.getDeclaredMethod("initDimension", int.class); |
| } catch(NoSuchMethodException e) { |
| e.printStackTrace(); |
| sender.sendMessage(String.format("Failed to load worlds: %s: %s", e.getClass().getName(), e.getMessage())); |
| return; |
| } |
| for(Map.Entry<Integer, Class<?>> entry : world_providers.entrySet()) try { |
| Object provider = entry.getValue().newInstance(); |
| String name = (String)mc_world_provider_get_name_method.invoke(provider); |
| boolean is_loaded = false; |
| for(Object mc_world : forge_loaded_worlds.values()) { |
| String loaded_name = (String)mc_world_provider_get_name_method.invoke(mc_world_provider_field.get(mc_world)); |
| if(name.equals((String)mc_world_provider_get_name_method.invoke(mc_world_provider_field.get(mc_world)))) { |
| is_loaded = true; |
| break; |
| } |
| } |
| if(is_loaded) continue; |
| Integer world_id = entry.getKey(); |
| sender.sendMessage(String.format("Loading world %s (%d)...", name, world_id)); |
| try { |
| forge_world_manager_load_world_method.invoke(null, world_id); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| if(e instanceof InvocationTargetException) { |
| Throwable cause = ((InvocationTargetException)e).getCause(); |
| if(cause != null && cause instanceof Exception) e = (Exception)cause; |
| } |
| sender.sendMessage(String.format("Failed to load world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| } |
| } catch(Exception e) { |
| e.printStackTrace(); |
| if(e instanceof InvocationTargetException) { |
| Throwable cause = ((InvocationTargetException)e).getCause(); |
| if(cause != null && cause instanceof Exception) e = (Exception)cause; |
| } |
| sender.sendMessage(e.getMessage()); |
| } |
| } |
| |
| private static Boolean parse_off_on_value(String s) { |
| if(s.equalsIgnoreCase("off")) return Boolean.valueOf(false); |
| if(s.equalsIgnoreCase("on")) return Boolean.valueOf(true); |
| return null; |
| } |
| |
| private static boolean is_valid_world_name(String name) { |
| int i = name.length(); |
| if(i < 1) return false; |
| if(name.charAt(0) == '.') return false; |
| do { |
| switch(name.charAt(--i)) { |
| case 0x4: |
| case '\b': |
| case ' ': |
| case '\n': |
| case '': |
| case '': |
| case '\r': |
| case ' ': |
| case 0x1b: |
| case '"': |
| case '#': |
| case '\'': |
| case '*': |
| case '/': |
| case ':': |
| case '\\': |
| case 0x7f: |
| return false; |
| } |
| } while(i > 0); |
| return true; |
| } |
| |
| private static int[] parse_coordinate(String s, int dimensions) { |
| try { |
| String[] p = s.split(","); |
| if(p.length != dimensions) throw new IllegalArgumentException(); |
| int[] coordinate = new int[dimensions]; |
| for(int i = 0; i < 3; i++) coordinate[i] = Integer.parseInt(p[i]); |
| return coordinate; |
| } catch(Exception e) { |
| return null; |
| } |
| } |
| |
| private static boolean is_symbolic_link(File file) throws IOException { |
| String[] args = new String[] { "[", "-h", file.toString(), "]" }; |
| Process p; |
| try { |
| p = Runtime.getRuntime().exec(args); |
| } catch(IOException e) { |
| args[0] = "/usr/bin/["; |
| try { |
| p = Runtime.getRuntime().exec(args); |
| } catch(IOException ee) { |
| throw e; |
| } |
| } |
| try { |
| p.waitFor(); |
| } catch(InterruptedException e) { |
| e.printStackTrace(); |
| } |
| try { |
| return p.exitValue() == 0; |
| } catch(IllegalThreadStateException e) { |
| e.printStackTrace(); |
| return false; |
| } |
| } |
| |
| private static void print_create_usage(CommandSender sender) { |
| StringBuilder usage_string = new StringBuilder("Usage: world create [<options>] <name>"); |
| usage_string.append("\nOptions:"); |
| usage_string.append("\n -t, --type {"); |
| WorldType[] types = WorldType.values(); |
| for(int i = 0; i < types.length; i++) { |
| if(i != 0) usage_string.append("|"); |
| usage_string.append(types[i].getName().toLowerCase()); |
| } |
| usage_string.append("}"); |
| usage_string.append("\n -s, --seed <seed-number>"); |
| usage_string.append("\n -S, --generate-structures {on|off}"); |
| if(is_minecraftforge_available()) { |
| usage_string.append("\n --ground-level <ground-level-number>"); |
| usage_string.append("\n --spawn-point <x>,<y>,<z>"); |
| usage_string.append("\n --max-height <height-number>"); |
| usage_string.append("\n --force-snow"); |
| usage_string.append("\n --no-lighting"); |
| usage_string.append("\n --no-snow-and-ice"); |
| } |
| if(is_bukkit_worldborder_interface_available()) { |
| usage_string.append("\n --border-center <x>,<z>"); |
| usage_string.append("\n --border-size <size-number>"); |
| } |
| sender.sendMessage(usage_string.toString()); |
| } |
| |
| private static final String LOAD_USAGE = "Usage: world load { -a | <name> }"; |
| private static final String UNLOAD_USAGE = "Usage: world unload [-n] {<name>|<uuid>} [...]"; |
| private static final String SWITCH_USAGE = "Usage: world switch [-c | -p <x>,<y>,<z>] {<name>|<uuid>}"; |
| |
| @Override |
| public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { |
| //"Usage:\n snapshot take [mc-]<name>\n snapshot drop mc-<name>\n snapshot list" |
| if(args.length < 1) return false; |
| if(args[0].equals("list")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.list")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| list_loaded_worlds(sender); |
| list_unloaded_worlds(sender); |
| } else if(args[0].equals("load")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.load_unload")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| String world_name; |
| if(args.length > 1 && args[1].charAt(0) == '-') switch(args[1].charAt(1)) { |
| case '-': |
| if(args[1].length() > 2 || args.length != 3) { |
| sender.sendMessage(LOAD_USAGE); |
| return true; |
| } |
| world_name = args[2]; |
| break; |
| case 'a': |
| if(is_minecraftforge_available()) { |
| load_all_worlds(sender); |
| } else { |
| sender.sendMessage("Operation not permitted"); |
| } |
| return true; |
| default: |
| sender.sendMessage(LOAD_USAGE); |
| return true; |
| } else if(args.length != 2) { |
| sender.sendMessage(LOAD_USAGE); |
| return true; |
| } else { |
| world_name = args[1]; |
| } |
| if(is_minecraftforge_available()) { |
| load_world(sender, world_name); |
| } else { |
| sender.sendMessage("Operation not permitted"); |
| } |
| } else if(args[0].equals("unload")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.load_unload")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| ArrayList<String> list = new ArrayList<String>(1); |
| boolean no_sync = false; |
| boolean end_of_options = false; |
| for(int i = 1; i < args.length; i++) { |
| if(!end_of_options && args[i].charAt(0) == '-') switch(args[i].charAt(1)) { |
| case '-': |
| if(args[i].length() > 2 || args.length < 3) { |
| sender.sendMessage(LOAD_USAGE); |
| return true; |
| } |
| end_of_options = true; |
| break; |
| case 'n': |
| no_sync = true; |
| break; |
| default: |
| sender.sendMessage(UNLOAD_USAGE); |
| return true; |
| } else list.add(args[i]); |
| } |
| if(list.isEmpty()) { |
| sender.sendMessage(UNLOAD_USAGE); |
| return true; |
| } |
| for(String name : list) { |
| World world = get_world_by_name_or_uuid(name); |
| if(world == null) { |
| sender.sendMessage(String.format("World %s not found", name)); |
| } else if(!server.unloadWorld(world, !no_sync)) { |
| sender.sendMessage("Failed to unload world " + name); |
| } |
| } |
| } else if(args[0].equals("save") || args[0].equals("sync")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.save")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| ArrayList<String> list = new ArrayList<String>(1); |
| boolean no_sync = false; |
| boolean end_of_options = false; |
| for(int i = 1; i < args.length; i++) { |
| if(!end_of_options && args[i].charAt(0) == '-') switch(args[i].charAt(1)) { |
| case '-': |
| if(args[i].length() > 2) { |
| sender.sendMessage(String.format("Invalid option '%s'", args[i])); |
| return true; |
| } |
| end_of_options = true; |
| break; |
| case 'a': |
| for(World world : server.getWorlds()) { |
| //sender.sendMessage(String.format("Synchronizing world %s (%s)...", world.getName(), world.getUID().toString())); |
| world.save(); |
| } |
| return true; |
| default: |
| sender.sendMessage(String.format("Invalid option '-%c'", args[i].charAt(1))); |
| return true; |
| } else list.add(args[i]); |
| } |
| if(list.isEmpty()) { |
| sender.sendMessage(String.format("Usage: world %s { -a | {<name>|<uuid>} [...] }", args[0])); |
| return true; |
| } |
| for(String name : list) { |
| World world = get_world_by_name_or_uuid(name); |
| if(world == null) { |
| sender.sendMessage(String.format("World %s not found", name)); |
| } else { |
| world.save(); |
| } |
| } |
| } else if(args[0].equals("switch")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.switch")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| if(!(sender instanceof Player)) { |
| sender.sendMessage("switch subcommand may only be used by players"); |
| return true; |
| } |
| String world_name = null; |
| boolean use_current_position = false; |
| int[] position = null; |
| boolean end_of_options = false; |
| for(int i = 1; i < args.length; i++) { |
| if(!end_of_options && args[i].charAt(0) == '-') switch(args[i].charAt(1)) { |
| case '-': |
| if(args[i].length() > 2) { |
| if(args[i].equals("--current-position")) { |
| use_current_position = true; |
| } else if(args[i].equals("--position")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--position' requires an argument"); |
| return true; |
| } |
| position = parse_coordinate(args[i], 3); |
| if(position == null) { |
| sender.sendMessage("Invalid coordinate specification"); |
| return true; |
| } |
| } else { |
| sender.sendMessage(String.format("Invalid option '%s'", args[i])); |
| return true; |
| } |
| } else end_of_options = true; |
| break; |
| case 'c': |
| use_current_position = true; |
| break; |
| case 'p': |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '-p' requires an argument"); |
| return true; |
| } |
| position = parse_coordinate(args[i], 3); |
| if(position == null) { |
| sender.sendMessage("Invalid coordinate specification"); |
| return true; |
| } |
| break; |
| default: |
| sender.sendMessage(String.format("Invalid option '-%c'", args[i].charAt(1))); |
| return true; |
| } else if(world_name == null) { |
| world_name = args[i]; |
| } else { |
| sender.sendMessage(SWITCH_USAGE); |
| return true; |
| } |
| } |
| if(world_name == null) { |
| sender.sendMessage(SWITCH_USAGE); |
| return true; |
| } |
| if(use_current_position && position != null) { |
| sender.sendMessage("Options '-c' and '-p' cannot be used at same time"); |
| return true; |
| } |
| if(position != null && !sender.hasPermission("rivoreo.worldmgr.switch_coordinate")) { |
| sender.sendMessage("You don't have permission to specify a coordinate"); |
| return true; |
| } |
| if(use_current_position && !sender.hasPermission("rivoreo.worldmgr.switch_current_position")) { |
| sender.sendMessage("You don't have permission to use your current position"); |
| return true; |
| } |
| World world = get_world_by_name_or_uuid(world_name); |
| if(world == null) { |
| if(is_minecraftforge_available()) { |
| world = load_world(sender, world_name); |
| if(world == null) return true; |
| } else { |
| sender.sendMessage(String.format("World %s not loaded", world_name)); |
| return true; |
| } |
| } |
| Player player = ((Player)sender); |
| World previous_world = player.getWorld(); |
| if(world == previous_world) { |
| sender.sendMessage("You are already in world " + world_name); |
| return true; |
| } |
| Location location; |
| if(use_current_position) { |
| double factor = Double.NaN; |
| if(is_minecraftforge_available()) try { |
| if(mc_world_provider_get_movement_factor_method == null) { |
| mc_world_provider_get_movement_factor_method = mc_world_provider_class.getDeclaredMethod("getMovementFactor"); |
| } |
| Object previous_provider = get_world_provider(previous_world); |
| Object new_provider = get_world_provider(world); |
| Double previous_factor = (Double)mc_world_provider_get_movement_factor_method.invoke(previous_provider); |
| Double new_factor = (Double)mc_world_provider_get_movement_factor_method.invoke(new_provider); |
| factor = previous_factor.doubleValue() / new_factor.doubleValue(); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| } |
| if(Double.isNaN(factor)) { |
| World.Environment previous_environment = previous_world.getEnvironment(); |
| World.Environment new_environment = world.getEnvironment(); |
| if(previous_environment != World.Environment.NETHER && new_environment == World.Environment.NETHER) { |
| factor = 1D / 8D; |
| } else if(previous_environment == World.Environment.NETHER && new_environment != World.Environment.NETHER) { |
| factor = 8D; |
| } else { |
| factor = 1D; |
| } |
| } |
| Location current = player.getLocation(); |
| double x = current.getBlockX() * factor + 0.5; |
| int y = current.getBlockY(); |
| double z = current.getBlockZ() * factor + 0.5; |
| boolean search_downward_wrapped = false; |
| while(true) { |
| int block_x = Location.locToBlock(x); |
| int block_z = Location.locToBlock(z); |
| if(world.getBlockAt(block_x, y, block_z).getType() == Material.AIR) { |
| if(y < 1) { |
| if(search_downward_wrapped) { |
| y = current.getBlockY(); |
| break; |
| } |
| y = 255; |
| search_downward_wrapped = true; |
| } else if(world.getBlockAt(block_x, --y, block_z).getType() != Material.AIR) { |
| y++; |
| break; |
| } |
| } else { |
| y++; |
| } |
| } |
| location = new Location(world, x, y, z); |
| } else if(position == null) { |
| location = world.getSpawnLocation(); |
| } else { |
| location = new Location(world, position[0], position[1], position[2]); |
| } |
| player.teleport(location, PlayerTeleportEvent.TeleportCause.COMMAND); |
| } else if(args[0].equals("create")) { |
| if(!sender.hasPermission("rivoreo.worldmgr.create")) { |
| sender.sendMessage("Permission denied"); |
| return true; |
| } |
| String name = null; |
| WorldType type = null; |
| Long seed = null; |
| Boolean should_generate_structures = null; |
| Integer ground_level = null; |
| Integer[] spawn_point = null; |
| Integer max_height = null; |
| boolean is_force_snow = false; |
| boolean allow_lighting = true; |
| boolean allow_snow_and_ice = true; |
| Double[] border_center = null; |
| Double border_size = null; |
| boolean end_of_options = false; |
| for(int i = 1; i < args.length; i++) { |
| if(!end_of_options && args[i].charAt(0) == '-') switch(args[i].charAt(1)) { |
| case '-': |
| if(args[i].length() > 2) { |
| if(args[i].equals("--type")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--type' requires an argument"); |
| return true; |
| } |
| try { |
| type = WorldType.valueOf(args[i].toUpperCase()); |
| } catch(IllegalArgumentException e) { |
| sender.sendMessage(String.format("Invalid world type '%s'", args[i])); |
| return true; |
| } |
| } else if(args[i].equals("--seed")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--seed' requires an argument"); |
| return true; |
| } |
| try { |
| seed = Long.decode(args[i]); |
| } catch(NumberFormatException e) { |
| sender.sendMessage("Seed must be a number"); |
| return true; |
| } |
| } else if(args[i].equals("--generate-structures")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--generate-structures' requires an argument"); |
| return true; |
| } |
| should_generate_structures = parse_off_on_value(args[i]); |
| if(should_generate_structures == null) { |
| sender.sendMessage("Invalid argument to '-S', must be 'off' or 'on'"); |
| return true; |
| } |
| } else if(args[i].equals("--ground-level")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--ground-level' requires an argument"); |
| return true; |
| } |
| try { |
| ground_level = Integer.valueOf(args[i]); |
| } catch(NumberFormatException e) { |
| sender.sendMessage("Invalid argument to '--ground-level'"); |
| return true; |
| } |
| if(ground_level.intValue() < 0) { |
| sender.sendMessage("Ground level cannot be negative"); |
| return true; |
| } |
| } else if(args[i].equals("--spawn-point")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--spawn-point' requires an argument"); |
| return true; |
| } |
| try { |
| String[] p = args[i].split(","); |
| if(p.length != 3) { |
| throw new IllegalArgumentException(); |
| } |
| spawn_point = new Integer[3]; |
| for(int j=0; j<3; j++) { |
| spawn_point[j] = Integer.valueOf(p[j]); |
| } |
| } catch(Exception e) { |
| sender.sendMessage("Invalid argument to '--spawn-point'"); |
| return true; |
| } |
| } else if(args[i].equals("--max-height")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--max-height' requires an argument"); |
| return true; |
| } |
| try { |
| max_height = Integer.decode(args[i]); |
| } catch(NumberFormatException e) { |
| sender.sendMessage("Invalid argument to '--max-height'"); |
| return true; |
| } |
| } else if(args[i].equals("--force-snow")) { |
| is_force_snow = true; |
| } else if(args[i].equals("--no-lighting")) { |
| allow_lighting = false; |
| } else if(args[i].equals("--no-snow-and-ice")) { |
| allow_snow_and_ice = false; |
| } else if(args[i].equals("--border-center")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--border-center' requires an argument"); |
| return true; |
| } |
| try { |
| String[] p = args[i].split(","); |
| if(p.length != 2) { |
| throw new IllegalArgumentException(); |
| } |
| border_center = new Double[] { |
| Double.valueOf(p[0]), |
| Double.valueOf(p[1]) |
| }; |
| } catch(Exception e) { |
| sender.sendMessage("Invalid argument to '--border-center'"); |
| return true; |
| } |
| } else if(args[i].equals("--border-size")) { |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '--border-size' requires an argument"); |
| return true; |
| } |
| try { |
| border_size = Double.valueOf(args[i]); |
| } catch(Exception e) { |
| sender.sendMessage("Invalid argument to '--border-size'"); |
| return true; |
| } |
| } else { |
| sender.sendMessage(String.format("Invalid option '%s'", args[i])); |
| return true; |
| } |
| } else end_of_options = true; |
| break; |
| case 't': |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '-t' requires an argument"); |
| return true; |
| } |
| try { |
| type = WorldType.valueOf(args[i].toUpperCase()); |
| } catch(IllegalArgumentException e) { |
| sender.sendMessage(String.format("Invalid world type '%s'", args[i])); |
| return true; |
| } |
| break; |
| case 's': |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '-s' requires an argument"); |
| return true; |
| } |
| try { |
| seed = Long.decode(args[i]); |
| } catch(NumberFormatException e) { |
| sender.sendMessage("Seed must be a number"); |
| return true; |
| } |
| break; |
| case 'S': |
| if(args.length <= ++i) { |
| sender.sendMessage("Option '-S' requires an argument"); |
| return true; |
| } |
| should_generate_structures = parse_off_on_value(args[i]); |
| if(should_generate_structures == null) { |
| sender.sendMessage("Invalid argument to '-S', must be 'off' or 'on'"); |
| return true; |
| } |
| break; |
| default: |
| sender.sendMessage(String.format("Invalid option '-%c'", args[i].charAt(1))); |
| return true; |
| } else if(name == null) { |
| name = args[i]; |
| if(!is_valid_world_name(name)) { |
| sender.sendMessage("Invalid world name"); |
| return true; |
| } |
| } else { |
| print_create_usage(sender); |
| return true; |
| } |
| } |
| if(name == null) { |
| print_create_usage(sender); |
| return true; |
| } |
| if(get_world_by_name_or_uuid(name) != null) { |
| sender.sendMessage(String.format("World %s is already loaded", name)); |
| return true; |
| } |
| try { |
| File save_dir = new File(server.getWorldContainer().getCanonicalFile(), name); |
| if(is_symbolic_link(save_dir)) { |
| sender.sendMessage(String.format("Failed to create world %s: target save directory '%s' is a symbolic link", name, save_dir)); |
| return true; |
| } |
| } catch(IOException e) { |
| e.printStackTrace(); |
| sender.sendMessage(String.format("Failed to create world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| return true; |
| } |
| if(is_force_snow && !allow_snow_and_ice) { |
| sender.sendMessage("Options '--force-snow' and '--no-snow-and-ice' cannot be used at same time"); |
| return true; |
| } |
| WorldCreator creator = new WorldCreator(name); |
| if(type != null) creator.type(type); |
| if(seed != null) creator.seed(seed.longValue()); |
| if(should_generate_structures != null) creator.generateStructures(should_generate_structures.booleanValue()); |
| if(is_minecraftforge_available()) try { |
| if(forge_world_id_map == null) { |
| Field world_id_map_field = forge_world_manager_class.getDeclaredField("dimensionMap"); |
| if(world_id_map_field.getType() != BitSet.class) throw new NoSuchFieldException("type mismatch"); |
| world_id_map_field.setAccessible(true); |
| forge_world_id_map = (BitSet)world_id_map_field.get(null); |
| } |
| Integer world_id = Integer.valueOf(forge_world_id_map.nextClearBit(0)); |
| if(customworldprovider_class == null) { |
| customworldprovider_class = Class.forName("rivoreo.minecraft.worldmgr.CustomWorldProvider"); |
| } |
| if(forge_register_world_provider_type_method == null) { |
| forge_register_world_provider_type_method = forge_world_manager_class.getDeclaredMethod("registerProviderType", int.class, Class.class, boolean.class); |
| } |
| forge_register_world_provider_type_method.invoke(null, world_id, customworldprovider_class, Boolean.valueOf(true)); |
| if(bukkit_world_environment_construtor == null) { |
| bukkit_world_environment_construtor = World.Environment.class.getDeclaredConstructor(String.class, int.class, int.class); |
| bukkit_world_environment_construtor.setAccessible(true); |
| } |
| World.Environment environment; |
| Object[] constructor_args = new Object[] { |
| name, Integer.valueOf(bukkit_world_environment_ordinal++), world_id |
| }; |
| try { |
| if(constructor_accessor_field == null) { |
| constructor_accessor_field = Constructor.class.getDeclaredField("constructorAccessor"); |
| constructor_accessor_field.setAccessible(true); |
| } |
| Object constructor_accessor = constructor_accessor_field.get(bukkit_world_environment_construtor); |
| if(constructor_accessor == null) { |
| if(acquire_constructor_accessor_method == null) { |
| acquire_constructor_accessor_method = Constructor.class.getDeclaredMethod("acquireConstructorAccessor"); |
| acquire_constructor_accessor_method.setAccessible(true); |
| } |
| constructor_accessor = acquire_constructor_accessor_method.invoke(bukkit_world_environment_construtor); |
| } |
| if(constructor_accessor_new_instance_method == null) { |
| constructor_accessor_new_instance_method = constructor_accessor.getClass().getDeclaredMethod("newInstance", constructor_args.getClass()); |
| constructor_accessor_new_instance_method.setAccessible(true); |
| } |
| environment = (World.Environment)constructor_accessor_new_instance_method.invoke(constructor_accessor, new Object[] { constructor_args }); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| environment = (World.Environment)bukkit_world_environment_construtor.newInstance(constructor_args); |
| } |
| creator.environment(environment); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| } |
| World world; |
| try { |
| world = creator.createWorld(); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| sender.sendMessage(String.format("Failed to create world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| return true; |
| } |
| if(customworldprovider_class != null) try { |
| Object provider = get_world_provider(world); |
| if(customworldprovider_class.isInstance(provider)) { |
| if(customworldprovider_set_world_name_method == null) { |
| customworldprovider_set_world_name_method = customworldprovider_class.getDeclaredMethod("set_world_name", String.class); |
| } |
| customworldprovider_set_world_name_method.invoke(provider, name); |
| /* Need to set environment back to |
| * NORMAL to prevent crashing client |
| * by sending an unknown world |
| * provider type ID. |
| * See Cauldron source file |
| * patches/net/minecraft/server/management/ServerConfigurationManager.java.patch |
| * comment |
| * 'Cauldron start - send DimensionRegisterMessage to client before attempting to login to a Bukkit dimension' |
| * for details. */ |
| if(craftbukkit_world_environment_field == null) { |
| craftbukkit_world_environment_field = world.getClass().getDeclaredField("environment"); |
| craftbukkit_world_environment_field.setAccessible(true); |
| } |
| craftbukkit_world_environment_field.set(world, World.Environment.NORMAL); |
| } else { |
| if(customworldprovider_constructor == null) { |
| customworldprovider_constructor = customworldprovider_class.getDeclaredConstructor(String.class); |
| } |
| provider = customworldprovider_constructor.newInstance(name); |
| Object mc_world = craftbukkit_world_get_handle_method.invoke(world); |
| int modifiers = mc_world_provider_field.getModifiers(); |
| if(Modifier.isFinal(modifiers)) try { |
| if(field_modifiers_field == null) { |
| field_modifiers_field = Field.class.getDeclaredField("modifiers"); |
| field_modifiers_field.setAccessible(true); |
| } |
| field_modifiers_field.setInt(mc_world_provider_field, modifiers & ~Modifier.FINAL); |
| } catch(NoSuchFieldException e) { |
| e.printStackTrace(); |
| } catch(IllegalAccessException e) { |
| e.printStackTrace(); |
| } |
| mc_world_provider_field.set(mc_world, provider); |
| if(mc_world_provider_register_world_method == null) try { |
| mc_world_provider_register_world_method = mc_world_provider_class.getDeclaredMethod("func_76558_a", mc_world_class); |
| } catch(NoSuchMethodException e) { |
| mc_world_provider_register_world_method = mc_world_provider_class.getDeclaredMethod("registerWorld", mc_world_class); |
| } |
| mc_world_provider_register_world_method.invoke(provider, mc_world); |
| } |
| if(ground_level != null) { |
| if(customworldprovider_set_average_ground_level_method == null) { |
| customworldprovider_set_average_ground_level_method = customworldprovider_class.getDeclaredMethod("set_average_ground_level", int.class); |
| } |
| customworldprovider_set_average_ground_level_method.invoke(provider, ground_level); |
| } |
| if(spawn_point != null) { |
| if(mc_world_provider_set_spawn_point_method == null) { |
| // Forge added method |
| mc_world_provider_set_spawn_point_method = mc_world_provider_class.getDeclaredMethod("setSpawnPoint", int.class, int.class, int.class); |
| } |
| mc_world_provider_set_spawn_point_method.invoke(provider, (Object[])spawn_point); |
| } |
| if(max_height != null) { |
| if(customworldprovider_set_max_height_method == null) { |
| customworldprovider_set_max_height_method = customworldprovider_class.getDeclaredMethod("set_max_height", int.class); |
| } |
| customworldprovider_set_max_height_method.invoke(provider, max_height); |
| } |
| if(is_force_snow) { |
| if(customworldprovider_set_force_snow_method == null) { |
| customworldprovider_set_force_snow_method = customworldprovider_class.getDeclaredMethod("set_force_snow", boolean.class); |
| } |
| customworldprovider_set_force_snow_method.invoke(provider, Boolean.valueOf(true)); |
| } |
| if(!allow_lighting) { |
| if(customworldprovider_set_allow_lighting_method == null) { |
| customworldprovider_set_allow_lighting_method = customworldprovider_class.getDeclaredMethod("set_allow_lighting", boolean.class); |
| } |
| customworldprovider_set_allow_lighting_method.invoke(provider, Boolean.valueOf(false)); |
| } |
| if(!allow_snow_and_ice) { |
| if(customworldprovider_set_allow_snow_and_ice_method == null) { |
| customworldprovider_set_allow_snow_and_ice_method = customworldprovider_class.getDeclaredMethod("set_allow_snow_and_ice", boolean.class); |
| } |
| customworldprovider_set_allow_snow_and_ice_method.invoke(provider, Boolean.valueOf(false)); |
| } |
| } catch(Exception e) { |
| e.printStackTrace(); |
| } |
| if(border_center != null || border_size != null) { |
| if(!is_bukkit_worldborder_interface_available()) { |
| sender.sendMessage("World border is not supported on this Bukkit platform, ignoring associated options"); |
| } else try { |
| Object border = bukkit_world_get_world_border_method.invoke(world); |
| if(border_center != null) { |
| bukkit_worldborder_set_center_method.invoke(border, (Object[])border_center); |
| } |
| if(border_size != null) { |
| bukkit_worldborder_set_size_method.invoke(border, border_size); |
| } |
| } catch(Exception e) { |
| e.printStackTrace(); |
| sender.sendMessage(String.format("Failed to configure world border for world %s: %s: %s", name, e.getClass().getName(), e.getMessage())); |
| } |
| } |
| sender.sendMessage(String.format("World %s (%s) loaded", name, world.getUID().toString())); |
| } else { |
| sender.sendMessage("Invalid subcommand"); |
| } |
| return true; |
| } |
| |
| @Override |
| public void onLoad() { |
| log = getLogger(); |
| server = getServer(); |
| } |
| |
| @Override |
| public void onEnable() { |
| getCommand("world").setExecutor(this); |
| } |
| } |