blob: 1716dfb032f03efc1d45f6180f19b87279c048f4 [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.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.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 Class<?> customworldprovider_class;
private static Method customworldprovider_set_world_name_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 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;
}
// 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;
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 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 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 CREATE_USAGE = "Usage: world create [-t <type>] [-s <seed-number>] [-S {on|off}] <name>";
@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;
}
if(args.length != 2 && args.length != 5) {
sender.sendMessage("Usage: world switch {<name>|<uuid>} [<x> <y> <z>]");
return true;
}
if(args.length == 5 && !sender.hasPermission("rivoreo.worldmgr.switch_coordinate")) {
sender.sendMessage("You don't have permission to specify a coordinate");
return true;
}
World world = get_world_by_name_or_uuid(args[1]);
if(world == null) {
if(is_minecraftforge_available()) {
world = load_world(sender, args[1]);
if(world == null) return true;
} else {
sender.sendMessage(String.format("World %s not loaded", args[1]));
return true;
}
}
Player player = ((Player)sender);
if(world == player.getWorld()) {
sender.sendMessage("You are already in world " + args[1]);
return true;
}
Location location;
if(args.length == 2) {
location = world.getSpawnLocation();
} else try {
location = new Location(world, Integer.parseInt(args[2]), Integer.parseInt(args[3]), Integer.parseInt(args[4]));
} catch(NumberFormatException e) {
sender.sendMessage("Invalid coordinate specification");
return true;
}
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;
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 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 {
sender.sendMessage(CREATE_USAGE);
return true;
}
}
if(name == null) {
sender.sendMessage(CREATE_USAGE);
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;
}
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);
}
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);
}
Object[] constructor_args = new Object[] {
name, Integer.valueOf(bukkit_world_environment_ordinal++), world_id
};
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);
}
World.Environment environment = (World.Environment)constructor_accessor_new_instance_method.invoke(constructor_accessor, new Object[] { 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);
if(craftbukkit_world_environment_field == null) {
craftbukkit_world_environment_field = world.getClass().getDeclaredField("environment");
craftbukkit_world_environment_field.setAccessible(true);
}
/* 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. */
craftbukkit_world_environment_field.set(world, World.Environment.NORMAL);
}
} catch(Exception e) {
e.printStackTrace();
}
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);
}
}