blob: 4f0c71a5615e9b93cf846d3a5c7a9ed9ffe0299e [file] [log] [blame] [raw]
/* 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.HashMap;
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_get_center_method;
private static Method bukkit_worldborder_set_center_method;
private static Method bukkit_worldborder_get_size_method;
private static Method bukkit_worldborder_set_size_method;
private static Field craftbukkit_worldborder_handle_field;
private static Class<?> mc_worldborder_class;
private static Method mc_worldborder_set_size_method;
private static Method mc_worldborder_set_maximum_coordinate_value_method;
private static Field mc_worldborder_maximum_coordinate_value_field;
private Map<String, BukkitWorldProperty> world_property_map;
private Logger log;
private Server server;
public 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_get_center_method == null || bukkit_worldborder_set_center_method == null || bukkit_worldborder_get_size_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_get_center_method == null) {
bukkit_worldborder_get_center_method = bukkit_worldborder_interface.getDeclaredMethod("getCenter");
}
if(bukkit_worldborder_set_center_method == null) {
bukkit_worldborder_set_center_method = bukkit_worldborder_interface.getDeclaredMethod("setCenter", double.class, double.class);
}
if(bukkit_worldborder_get_size_method == null) {
bukkit_worldborder_get_size_method = bukkit_worldborder_interface.getDeclaredMethod("getSize");
}
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
public 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;
}
// Cauldron only
public static void set_custom_world_provider_max_height(Object provider, Integer value) throws Exception {
if(customworldprovider_class == null) {
customworldprovider_class = Class.forName("rivoreo.minecraft.worldmgr.CustomWorldProvider");
}
if(!customworldprovider_class.isInstance(provider)) {
throw new UnsupportedOperationException("This operation requires customized world provider");
}
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, value);
}
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());
}
}
// CraftBukkit with WorldBorder support only
public static void maximize_world_border_limitation(Object border) {
if(craftbukkit_worldborder_handle_field == null) try {
Class<?> craftbukkit_worldborder_class = border.getClass();
if(!craftbukkit_worldborder_class.getName().endsWith(".CraftWorldBorder")) {
return;
}
craftbukkit_worldborder_handle_field = craftbukkit_worldborder_class.getDeclaredField("handle");
craftbukkit_worldborder_handle_field.setAccessible(true);
} catch(NoSuchFieldException e) {
return;
}
Object mc_world_border;
try {
mc_world_border = craftbukkit_worldborder_handle_field.get(border);
} catch(IllegalAccessException e) {
e.printStackTrace();
return;
}
if(mc_worldborder_set_maximum_coordinate_value_method == null || mc_worldborder_maximum_coordinate_value_field == null) {
mc_worldborder_class = craftbukkit_worldborder_handle_field.getType();
if(mc_worldborder_set_maximum_coordinate_value_method == null) try {
mc_worldborder_set_maximum_coordinate_value_method = mc_worldborder_class.getDeclaredMethod("a", int.class);
if(mc_worldborder_set_maximum_coordinate_value_method.getReturnType() != void.class) {
mc_worldborder_set_maximum_coordinate_value_method = null;
}
} catch(NoSuchMethodException e) {
e.printStackTrace();
}
if(mc_worldborder_maximum_coordinate_value_field == null) try {
mc_worldborder_maximum_coordinate_value_field = mc_worldborder_class.getDeclaredField("h");
if(mc_worldborder_maximum_coordinate_value_field.getType() == int.class) {
mc_worldborder_maximum_coordinate_value_field.setAccessible(true);
} else {
mc_worldborder_maximum_coordinate_value_field = null;
}
} catch(NoSuchFieldException e) {
e.printStackTrace();
}
}
if(mc_worldborder_set_maximum_coordinate_value_method != null) try {
mc_worldborder_set_maximum_coordinate_value_method.invoke(mc_world_border, Integer.valueOf(Integer.MAX_VALUE));
} catch(IllegalAccessException e) {
e.printStackTrace();
} catch(InvocationTargetException e) {
e.printStackTrace();
}
if(mc_worldborder_maximum_coordinate_value_field != null) try {
int value = mc_worldborder_maximum_coordinate_value_field.getInt(mc_world_border);
if(value == 29999984) {
mc_worldborder_maximum_coordinate_value_field.setInt(mc_world_border, Integer.valueOf(Integer.MAX_VALUE));
}
} catch(IllegalAccessException e) {
e.printStackTrace();
}
}
// CraftBukkit with WorldBorder support only
public static void set_world_border_size(Object border, Double size) throws IllegalAccessException, InvocationTargetException {
try {
if(craftbukkit_worldborder_handle_field == null) {
Class<?> craftbukkit_worldborder_class = border.getClass();
if(!craftbukkit_worldborder_class.getName().endsWith(".CraftWorldBorder")) {
throw new ClassNotFoundException(".CraftWorldBorder");
}
craftbukkit_worldborder_handle_field = craftbukkit_worldborder_class.getDeclaredField("handle");
craftbukkit_worldborder_handle_field.setAccessible(true);
}
Object mc_world_border = craftbukkit_worldborder_handle_field.get(border);
if(mc_worldborder_set_size_method == null) {
mc_worldborder_set_size_method = mc_worldborder_class.getDeclaredMethod("setSize", double.class);
}
mc_worldborder_set_size_method.invoke(mc_world_border, size);
} catch(Exception e) {
bukkit_worldborder_set_size_method.invoke(border, size);
}
}
private void init_world_property_map() {
world_property_map = new HashMap<String, BukkitWorldProperty>();
world_property_map.put("time", new BukkitWorldTimeProperty());
world_property_map.put("seed", new BukkitWorldSeedProperty());
world_property_map.put("autosave", new BukkitWorldAutoSaveProperty());
world_property_map.put("thundering", new BukkitWorldThunderingProperty());
world_property_map.put("max_height", new BukkitWorldMaxHeightProperty());
if(is_bukkit_worldborder_interface_available()) {
world_property_map.put("border_center", new BukkitWorldBorderCenterProperty(bukkit_world_get_world_border_method, bukkit_worldborder_get_center_method, bukkit_worldborder_set_center_method));
world_property_map.put("border_size", new BukkitWorldBorderSizeProperty(bukkit_world_get_world_border_method, bukkit_worldborder_get_size_method));
}
}
private void print_all_world_properties(CommandSender sender, World world) {
for(Map.Entry<String, BukkitWorldProperty> entry : world_property_map.entrySet()) try {
sender.sendMessage(String.format("%s=%s", entry.getKey(), entry.getValue().get(world)));
} catch(Exception e) {
sender.sendMessage(String.format("Failed to get %s: %s: %s", entry.getKey(), e.getClass().getName(), e.getMessage()));
}
}
private void print_world_property(CommandSender sender, World world, String prop_name) {
BukkitWorldProperty prop = world_property_map.get(prop_name);
if(prop == null) {
sender.sendMessage(String.format("Unknown property '%s'", prop_name));
} else try {
sender.sendMessage(String.format("%s=%s", prop_name, prop.get(world)));
} catch(Exception e) {
sender.sendMessage(String.format("Failed to get %s: %s: %s", prop_name, e.getClass().getName(), e.getMessage()));
}
}
private void set_world_property(CommandSender sender, World world, String prop_name, String value) {
BukkitWorldProperty prop = world_property_map.get(prop_name);
if(prop == null) {
sender.sendMessage(String.format("Unknown property '%s'", prop_name));
} else try {
prop.set(world, value);
} catch(Exception e) {
sender.sendMessage(String.format("Failed to set %s: %s: %s", prop_name, e.getClass().getName(), e.getMessage()));
}
}
public static boolean parse_off_on_value(String s) {
if(s.equalsIgnoreCase("off")) return false;
if(s.equalsIgnoreCase("on")) return true;
throw new IllegalArgumentException("value must be 'off' or 'on'");
}
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 < dimensions; 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 void print_control_usage(CommandSender sender) {
StringBuilder usage_string = new StringBuilder("Usage: world control {<name>|<uuid>} { all | {<property>[=<value>]} [...] }");
usage_string.append("\nProperties:");
for(String prop_name : world_property_map.keySet()) {
usage_string.append("\n ").append(prop_name);
}
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 = world.getMaxHeight();
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 = true;
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;
}
try {
should_generate_structures = parse_off_on_value(args[i]);
} catch(IllegalArgumentException e) {
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;
}
try {
should_generate_structures = parse_off_on_value(args[i]);
} catch(IllegalArgumentException e) {
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) creator.generateStructures(false);
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) {
set_custom_world_provider_max_height(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);
maximize_world_border_limitation(border);
if(border_center != null) {
bukkit_worldborder_set_center_method.invoke(border, (Object[])border_center);
}
if(border_size != null) {
set_world_border_size(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 if(args[0].equals("control")) {
if(!sender.hasPermission("rivoreo.worldmgr.control")) {
sender.sendMessage("Permission denied");
return true;
}
if(world_property_map == null) init_world_property_map();
if(args.length < 3) {
print_control_usage(sender);
return true;
}
World world = get_world_by_name_or_uuid(args[1]);
if(world == null) {
sender.sendMessage(String.format("World %s not found", args[1]));
return true;
}
if(args.length == 3 && args[2].equals("all")) {
print_all_world_properties(sender, world);
return true;
}
for(int i = 2; i < args.length; i++) {
int equal_i = args[i].indexOf('=');
if(equal_i < 0) {
print_world_property(sender, world, args[i]);
} else if(sender.hasPermission("rivoreo.worldmgr.control.set")) {
set_world_property(sender, world, args[i].substring(0, equal_i), args[i].substring(equal_i + 1));
} else {
sender.sendMessage(args[i] + ": Permission denied");
}
}
} else {
sender.sendMessage("Invalid subcommand");
}
return true;
}
@Override
public void onLoad() {
log = getLogger();
server = getServer();
}
@Override
public void onEnable() {
getCommand("world").setExecutor(this);
}
}