package com.bergerkiller.bukkit.common.server;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.bukkit.Bukkit;
import com.bergerkiller.bukkit.common.reflection.ClassTemplate;
import com.bergerkiller.bukkit.common.reflection.MethodAccessor;
import com.bergerkiller.bukkit.common.reflection.SafeField;
import com.bergerkiller.bukkit.common.utils.StreamUtil;
public class MCPCPlusServer extends SpigotServer {
private Object classRemapper;
private MethodAccessor<String> mapType;
private MethodAccessor<String> mapField;
private Map<String, String> classesMap;
private Map<String, String> methodsMap;
private boolean isRelocatedSpigotUtils;
@Override
public boolean init() {
if (!super.init() || (!Bukkit.getServer().getVersion().contains("MCPC-Plus") && !Bukkit.getServer().getVersion().contains("Cauldron"))) {
return false;
}
// Obtain the Class remapper used by MCPC+
this.classRemapper = SafeField.get(getClass().getClassLoader(), "remapper");
if (this.classRemapper == null) {
throw new RuntimeException("Running an MCPC+ server but the remapper is unavailable...please turn it on!");
}
// Initialize some fields and methods used by the Jar Remapper
ClassTemplate<?> template = ClassTemplate.create(this.classRemapper);
this.mapType = template.getMethod("map", String.class);
this.mapField = template.getMethod("mapFieldName", String.class, String.class, String.class);
Object jarMapping = SafeField.get(classRemapper, "jarMapping");
this.classesMap = SafeField.get(jarMapping, "classes");
this.methodsMap = SafeField.get(jarMapping, "methods");
// Check whether the Spigot utilities are relocated
try {
Class.forName("org.spigotmc.FlatMap");
isRelocatedSpigotUtils = true;
} catch (ClassNotFoundException ex) {
isRelocatedSpigotUtils = false;
}
return true;
}
@Override
public String getServerName() {
return "MCPC+";
}
@Override
public String getClassName(String path) {
if (!isRelocatedSpigotUtils && path.equals("org.spigotmc.FlatMap")) {
return CB_ROOT_VERSIONED + ".util.FlatMap";
}
return mapType.invoke(classRemapper, super.getClassName(path).replace('.', '/')).replace('/', '.');
}
private String getOriginalOwner(Class<?> type) {
String typeName = type.getName().replace('.', '/');
// Find the original type of the current version (PACKAGE_VERSION)
String result = typeName;
for (Map.Entry<String, String> entry : classesMap.entrySet()) {
if (entry.getValue().equals(typeName)) {
result = entry.getKey();
// If the perfect one is found, just use it
if (result.contains(PACKAGE_VERSION)) {
return result;
}
}
}
// Failure or perhaps we found something similar...we can only hope for success
return result;
}
@Override
public String getFieldName(Class<?> type, String fieldName) {
return mapField.invoke(classRemapper, getOriginalOwner(type), fieldName, "");
}
@Override
public String getMethodName(Class<?> type, String methodName, Class<?>... params) {
final String methodPath = getOriginalOwner(type) + "/" + methodName + " ";
for (Map.Entry<String, String> entry : methodsMap.entrySet()) {
// Try to find the (obfuscated) method, if it exists with the parameters, we found our method
// We can not use the JarRemapper to do this, because the methods map includes a return type
// We do not know the return type here, which makes that impossible to use
if (entry.getKey().startsWith(methodPath)) {
try {
type.getDeclaredMethod(entry.getValue(), params);
// Found our method, return the official method name
return entry.getValue();
} catch (Throwable t) {
// Method not found, go on...
}
}
}
Class<?> superClass = type.getSuperclass();
// Try to find the method in the super class
if (superClass != null) {
return getMethodName(superClass, methodName, params);
}
// Failure to replace anything, perhaps it is correct?
return methodName;
}
@Override
public File getWorldFolder(String worldName) {
File container = Bukkit.getWorldContainer();
if (container.getName().equalsIgnoreCase(worldName)) {
return container;
} else {
return StreamUtil.getFileIgnoreCase(container, worldName);
}
}
@Override
public Collection<String> getLoadableWorlds() {
File container = Bukkit.getWorldContainer();
String[] files = container.list();
Collection<String> rval = new ArrayList<String>(files.length);
// Add the main world
rval.add(container.getName());
// Add all sub-worlds found in there
for (String worldName : files) {
if (isLoadableWorld(worldName)) {
rval.add(worldName);
}
}
return rval;
}
@Override
public File getWorldRegionFolder(String worldName) {
File region = new File(getWorldFolder(worldName), "region");
return region.exists() ? region : null;
}
}