SPIGOT-7011, SPIGOT-7065: Overhaul of structures

By: DerFrZocker <derrieple@gmail.com>
This commit is contained in:
CraftBukkit/Spigot 2022-07-01 20:41:04 +10:00
parent 8bf76d9868
commit 230282ea44
11 changed files with 397 additions and 57 deletions

View file

@ -0,0 +1,74 @@
package org.bukkit.craftbukkit;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.generator.strucutre.CraftStructure;
import org.bukkit.craftbukkit.generator.strucutre.CraftStructureType;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
public static <B extends Keyed> Registry<?> createRegistry(Class<B> bukkitClass, IRegistryCustom registryHolder) {
if (bukkitClass == Structure.class) {
return new CraftRegistry<>(registryHolder.registryOrThrow(IRegistry.STRUCTURE_REGISTRY), CraftStructure::new);
}
if (bukkitClass == StructureType.class) {
return new CraftRegistry<>(IRegistry.STRUCTURE_TYPES, CraftStructureType::new);
}
return null;
}
private final Map<NamespacedKey, B> cache = new HashMap<>();
private final IRegistry<M> minecraftRegistry;
private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
public CraftRegistry(IRegistry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit) {
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;
}
@Override
public B get(NamespacedKey namespacedKey) {
B cached = cache.get(namespacedKey);
if (cached != null) {
return cached;
}
B bukkit = createBukkit(namespacedKey, minecraftRegistry.getOptional(CraftNamespacedKey.toMinecraft(namespacedKey)).orElse(null));
if (bukkit == null) {
return null;
}
cache.put(namespacedKey, bukkit);
return bukkit;
}
@Override
public Iterator<B> iterator() {
return values().iterator();
}
public B createBukkit(NamespacedKey namespacedKey, M minecraft) {
if (minecraft == null) {
return null;
}
return minecraftToBukkit.apply(namespacedKey, minecraft);
}
public Stream<B> values() {
return minecraftRegistry.keySet().stream().map(minecraftKey -> get(CraftNamespacedKey.fromMinecraft(minecraftKey)));
}
}

View file

@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -117,6 +118,7 @@ import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.Registry;
import org.bukkit.Server;
import org.bukkit.StructureType;
import org.bukkit.UnsafeValues;
@ -257,6 +259,7 @@ public final class CraftServer implements Server {
protected final DedicatedServer console;
protected final DedicatedPlayerList playerList;
private final Map<String, World> worlds = new LinkedHashMap<String, World>();
private final Map<Class<?>, Registry<?>> registries = new HashMap<>();
private YamlConfiguration configuration;
private YamlConfiguration commandsConfiguration;
private final Yaml yaml = new Yaml(new SafeConstructor());
@ -2260,6 +2263,11 @@ public final class CraftServer implements Server {
return structureManager;
}
@Override
public <T extends Keyed> Registry<T> getRegistry(Class<T> aClass) {
return (Registry<T>) registries.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, console.registryHolder));
}
@Deprecated
@Override
public UnsafeValues getUnsafe() {

View file

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
@ -25,7 +26,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.BlockPosition;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.HolderSet;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
@ -79,8 +80,8 @@ import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Raid;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.StructureType;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.WorldBorder;
@ -95,6 +96,7 @@ import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.boss.CraftDragonBattle;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.generator.strucutre.CraftStructure;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.metadata.BlockMetadataStore;
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
@ -104,6 +106,7 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.CraftRayTraceResult;
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
import org.bukkit.craftbukkit.util.CraftStructureSearchResult;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
@ -122,6 +125,8 @@ import org.bukkit.event.world.TimeSkipEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue;
@ -133,6 +138,7 @@ import org.bukkit.potion.PotionType;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Consumer;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.StructureSearchResult;
import org.bukkit.util.Vector;
public class CraftWorld extends CraftRegionAccessor implements World {
@ -1776,11 +1782,84 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
@Deprecated
@Override
public Location locateNearestStructure(Location origin, StructureType structureType, int radius, boolean findUnexplored) {
public Location locateNearestStructure(Location origin, org.bukkit.StructureType structureType, int radius, boolean findUnexplored) {
StructureSearchResult result = null;
// Manually map the mess of the old StructureType to the new StructureType and normal Structure
if (org.bukkit.StructureType.MINESHAFT == structureType) {
result = locateNearestStructure(origin, StructureType.MINESHAFT, radius, findUnexplored);
} else if (org.bukkit.StructureType.VILLAGE == structureType) {
result = locateNearestStructure(origin, List.of(Structure.VILLAGE_DESERT, Structure.VILLAGE_PLAINS, Structure.VILLAGE_SAVANNA, Structure.VILLAGE_SNOWY, Structure.VILLAGE_TAIGA), radius, findUnexplored);
} else if (org.bukkit.StructureType.NETHER_FORTRESS == structureType) {
result = locateNearestStructure(origin, StructureType.FORTRESS, radius, findUnexplored);
} else if (org.bukkit.StructureType.STRONGHOLD == structureType) {
result = locateNearestStructure(origin, StructureType.STRONGHOLD, radius, findUnexplored);
} else if (org.bukkit.StructureType.JUNGLE_PYRAMID == structureType) {
result = locateNearestStructure(origin, StructureType.JUNGLE_TEMPLE, radius, findUnexplored);
} else if (org.bukkit.StructureType.OCEAN_RUIN == structureType) {
result = locateNearestStructure(origin, StructureType.OCEAN_RUIN, radius, findUnexplored);
} else if (org.bukkit.StructureType.DESERT_PYRAMID == structureType) {
result = locateNearestStructure(origin, StructureType.DESERT_PYRAMID, radius, findUnexplored);
} else if (org.bukkit.StructureType.IGLOO == structureType) {
result = locateNearestStructure(origin, StructureType.IGLOO, radius, findUnexplored);
} else if (org.bukkit.StructureType.SWAMP_HUT == structureType) {
result = locateNearestStructure(origin, StructureType.SWAMP_HUT, radius, findUnexplored);
} else if (org.bukkit.StructureType.OCEAN_MONUMENT == structureType) {
result = locateNearestStructure(origin, StructureType.OCEAN_MONUMENT, radius, findUnexplored);
} else if (org.bukkit.StructureType.END_CITY == structureType) {
result = locateNearestStructure(origin, StructureType.END_CITY, radius, findUnexplored);
} else if (org.bukkit.StructureType.WOODLAND_MANSION == structureType) {
result = locateNearestStructure(origin, StructureType.WOODLAND_MANSION, radius, findUnexplored);
} else if (org.bukkit.StructureType.BURIED_TREASURE == structureType) {
result = locateNearestStructure(origin, StructureType.BURIED_TREASURE, radius, findUnexplored);
} else if (org.bukkit.StructureType.SHIPWRECK == structureType) {
result = locateNearestStructure(origin, StructureType.SHIPWRECK, radius, findUnexplored);
} else if (org.bukkit.StructureType.PILLAGER_OUTPOST == structureType) {
result = locateNearestStructure(origin, Structure.PILLAGER_OUTPOST, radius, findUnexplored);
} else if (org.bukkit.StructureType.NETHER_FOSSIL == structureType) {
result = locateNearestStructure(origin, StructureType.NETHER_FOSSIL, radius, findUnexplored);
} else if (org.bukkit.StructureType.RUINED_PORTAL == structureType) {
result = locateNearestStructure(origin, StructureType.RUINED_PORTAL, radius, findUnexplored);
} else if (org.bukkit.StructureType.BASTION_REMNANT == structureType) {
result = locateNearestStructure(origin, Structure.BASTION_REMNANT, radius, findUnexplored);
}
return (result == null) ? null : result.getLocation();
}
@Override
public StructureSearchResult locateNearestStructure(Location origin, StructureType structureType, int radius, boolean findUnexplored) {
List<Structure> structures = new ArrayList<>();
for (Structure structure : Registry.STRUCTURE) {
if (structure.getStructureType() == structureType) {
structures.add(structure);
}
}
return locateNearestStructure(origin, structures, radius, findUnexplored);
}
@Override
public StructureSearchResult locateNearestStructure(Location origin, Structure structure, int radius, boolean findUnexplored) {
return locateNearestStructure(origin, List.of(structure), radius, findUnexplored);
}
public StructureSearchResult locateNearestStructure(Location origin, List<Structure> structures, int radius, boolean findUnexplored) {
BlockPosition originPos = new BlockPosition(origin.getX(), origin.getY(), origin.getZ());
BlockPosition nearest = getHandle().findNearestMapStructure(TagKey.create(IRegistry.STRUCTURE_REGISTRY, CraftNamespacedKey.toMinecraft(structureType.getKey())), originPos, radius, findUnexplored);
return (nearest == null) ? null : new Location(this, nearest.getX(), nearest.getY(), nearest.getZ());
List<Holder<net.minecraft.world.level.levelgen.structure.Structure>> holders = new ArrayList<>();
for (Structure structure : structures) {
holders.add(Holder.direct(CraftStructure.bukkitToMinecraft(structure)));
}
Pair<BlockPosition, Holder<net.minecraft.world.level.levelgen.structure.Structure>> found = getHandle().getChunkSource().getGenerator().findNearestMapStructure(getHandle(), HolderSet.direct(holders), originPos, radius, findUnexplored);
if (found == null) {
return null;
}
return new CraftStructureSearchResult(CraftStructure.minecraftToBukkit(found.getSecond().value(), getHandle().registryAccess()), new Location(this, found.getFirst().getX(), found.getFirst().getY(), found.getFirst().getZ()));
}
@Override

View file

@ -0,0 +1,52 @@
package org.bukkit.craftbukkit.generator.strucutre;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
public class CraftStructure extends Structure {
public static Structure minecraftToBukkit(net.minecraft.world.level.levelgen.structure.Structure minecraft, IRegistryCustom registryHolder) {
if (minecraft == null) {
return null;
}
return Registry.STRUCTURE.get(CraftNamespacedKey.fromMinecraft(registryHolder.registryOrThrow(IRegistry.STRUCTURE_REGISTRY).getKey(minecraft)));
}
public static net.minecraft.world.level.levelgen.structure.Structure bukkitToMinecraft(Structure bukkit) {
if (bukkit == null) {
return null;
}
return ((CraftStructure) bukkit).getHandle();
}
private final NamespacedKey key;
private final net.minecraft.world.level.levelgen.structure.Structure structure;
private final StructureType structureType;
public CraftStructure(NamespacedKey key, net.minecraft.world.level.levelgen.structure.Structure structure) {
this.key = key;
this.structure = structure;
this.structureType = CraftStructureType.minecraftToBukkit(structure.type());
}
public net.minecraft.world.level.levelgen.structure.Structure getHandle() {
return structure;
}
@Override
public StructureType getStructureType() {
return structureType;
}
@Override
public NamespacedKey getKey() {
return key;
}
}

View file

@ -0,0 +1,43 @@
package org.bukkit.craftbukkit.generator.strucutre;
import net.minecraft.core.IRegistry;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.generator.structure.StructureType;
public class CraftStructureType extends StructureType {
public static StructureType minecraftToBukkit(net.minecraft.world.level.levelgen.structure.StructureType<?> minecraft) {
if (minecraft == null) {
return null;
}
return Registry.STRUCTURE_TYPE.get(CraftNamespacedKey.fromMinecraft(IRegistry.STRUCTURE_TYPES.getKey(minecraft)));
}
public static net.minecraft.world.level.levelgen.structure.StructureType<?> bukkitToMinecraft(StructureType bukkit) {
if (bukkit == null) {
return null;
}
return ((CraftStructureType) bukkit).getHandle();
}
private final NamespacedKey key;
private final net.minecraft.world.level.levelgen.structure.StructureType<?> structureType;
public CraftStructureType(NamespacedKey key, net.minecraft.world.level.levelgen.structure.StructureType<?> structureType) {
this.key = key;
this.structureType = structureType;
}
public net.minecraft.world.level.levelgen.structure.StructureType<?> getHandle() {
return structureType;
}
@Override
public NamespacedKey getKey() {
return key;
}
}

View file

@ -0,0 +1,24 @@
package org.bukkit.craftbukkit.util;
import org.bukkit.Location;
import org.bukkit.generator.structure.Structure;
import org.bukkit.util.StructureSearchResult;
public class CraftStructureSearchResult implements StructureSearchResult {
private final Structure structure;
private final Location location;
public CraftStructureSearchResult(Structure structure, Location location) {
this.structure = structure;
this.location = location;
}
public Structure getStructure() {
return structure;
}
public Location getLocation() {
return location;
}
}

View file

@ -1,49 +0,0 @@
package org.bukkit;
import java.util.Map;
import net.minecraft.core.IRegistry;
import net.minecraft.resources.MinecraftKey;
import org.bukkit.support.AbstractTestingBase;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
/**
* This test makes sure that Bukkit always has Minecraft structure types up to
* date.
*/
public class StructureTypeTest extends AbstractTestingBase {
private static Map<String, StructureType> structures;
@BeforeClass
public static void setUp() {
structures = StructureType.getStructureTypes();
}
@Test
@Ignore("Some types missing during unit test run")
public void testMinecraftToBukkit() {
for (MinecraftKey key : IRegistry.STRUCTURE_TYPES.keySet()) {
Assert.assertNotNull(key.getPath(), structures.get(key.getPath()));
}
}
@Test
public void testBukkit() {
for (Map.Entry<String, StructureType> entry : structures.entrySet()) {
Assert.assertNotNull(entry.getKey(), StructureType.getStructureTypes().get(entry.getKey()));
Assert.assertNotNull(entry.getValue().getName(), StructureType.getStructureTypes().get(entry.getValue().getName()));
}
}
@Test
@Ignore("Some types missing during unit test run")
public void testBukkitToMinecraft() {
for (Map.Entry<String, StructureType> entry : structures.entrySet()) {
Assert.assertNotNull(entry.getKey(), IRegistry.STRUCTURE_TYPES.get(new MinecraftKey(entry.getKey())));
Assert.assertNotNull(entry.getValue().getName(), IRegistry.STRUCTURE_TYPES.get(new MinecraftKey(entry.getValue().getName())));
}
}
}

View file

@ -0,0 +1,50 @@
package org.bukkit.generator.structure;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import net.minecraft.core.IRegistry;
import net.minecraft.resources.MinecraftKey;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.support.AbstractTestingBase;
import org.junit.Assert;
import org.junit.Test;
public class StructureTest extends AbstractTestingBase {
@Test
public void testBukkitToMinecraftFieldName() {
for (Field field : Structure.class.getFields()) {
if (field.getType() != Structure.class) {
continue;
}
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
String name = field.getName();
Assert.assertNotNull("No structure for field name " + name, Registry.STRUCTURE.get(NamespacedKey.fromString(name.toLowerCase())));
}
}
@Test
public void testMinecraftToBukkitFieldName() {
IRegistry<net.minecraft.world.level.levelgen.structure.Structure> structureIRegistry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(IRegistry.STRUCTURE_REGISTRY);
for (net.minecraft.world.level.levelgen.structure.Structure structure : structureIRegistry) {
MinecraftKey minecraftKey = structureIRegistry.getKey(structure);
try {
Structure bukkit = (Structure) Structure.class.getField(minecraftKey.getPath().toUpperCase()).get(null);
Assert.assertEquals("Keys are not the same for " + minecraftKey, minecraftKey, CraftNamespacedKey.toMinecraft(bukkit.getKey()));
} catch (NoSuchFieldException e) {
Assert.fail("No Bukkit default structure for " + minecraftKey);
} catch (IllegalAccessException e) {
Assert.fail("Bukkit field is not access able for " + minecraftKey);
} catch (ClassCastException e) {
Assert.fail("Bukkit field is not of type structure for" + minecraftKey);
}
}
}
}

View file

@ -0,0 +1,49 @@
package org.bukkit.generator.structure;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import net.minecraft.core.IRegistry;
import net.minecraft.resources.MinecraftKey;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.support.AbstractTestingBase;
import org.junit.Assert;
import org.junit.Test;
public class StructureTypeTest extends AbstractTestingBase {
@Test
public void testBukkitToMinecraftFieldName() {
for (Field field : StructureType.class.getFields()) {
if (field.getType() != StructureType.class) {
continue;
}
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
String name = field.getName();
Assert.assertNotNull("No structure type for field name " + name, Registry.STRUCTURE_TYPE.get(NamespacedKey.fromString(name.toLowerCase())));
}
}
@Test
public void testMinecraftToBukkitFieldName() {
for (net.minecraft.world.level.levelgen.structure.StructureType<?> structureType : IRegistry.STRUCTURE_TYPES) {
MinecraftKey minecraftKey = IRegistry.STRUCTURE_TYPES.getKey(structureType);
try {
StructureType bukkit = (StructureType) StructureType.class.getField(minecraftKey.getPath().toUpperCase()).get(null);
Assert.assertEquals("Keys are not the same for " + minecraftKey, minecraftKey, CraftNamespacedKey.toMinecraft(bukkit.getKey()));
} catch (NoSuchFieldException e) {
Assert.fail("No Bukkit default structure type for " + minecraftKey);
} catch (IllegalAccessException e) {
Assert.fail("Bukkit field is not access able for " + minecraftKey);
} catch (ClassCastException e) {
Assert.fail("Bukkit field is not of type structure type for" + minecraftKey);
}
}
}
}

View file

@ -30,6 +30,7 @@ public abstract class AbstractTestingBase {
public static final List<Material> INVALIDATED_MATERIALS;
public static final DataPackResources DATA_PACK;
public static final IRegistryCustom.Dimension REGISTRY_CUSTOM;
static {
SharedConstants.tryDetectVersion();
@ -37,11 +38,11 @@ public abstract class AbstractTestingBase {
// Set up resource manager
ResourceManager resourceManager = new ResourceManager(EnumResourcePackType.SERVER_DATA, Collections.singletonList(new ResourcePackVanilla(ResourcePackSourceVanilla.BUILT_IN_METADATA, "minecraft")));
// add tags and loot tables for unit tests
IRegistryCustom.Dimension registry = IRegistryCustom.builtinCopy().freeze();
REGISTRY_CUSTOM = IRegistryCustom.builtinCopy().freeze();
// Register vanilla pack
DATA_PACK = DataPackResources.loadResources(resourceManager, registry, CommandDispatcher.ServerType.DEDICATED, 0, MoreExecutors.directExecutor(), MoreExecutors.directExecutor()).join();
DATA_PACK = DataPackResources.loadResources(resourceManager, REGISTRY_CUSTOM, CommandDispatcher.ServerType.DEDICATED, 0, MoreExecutors.directExecutor(), MoreExecutors.directExecutor()).join();
// Bind tags
DATA_PACK.updateRegistryTags(registry);
DATA_PACK.updateRegistryTags(REGISTRY_CUSTOM);
DummyServer.setup();
DummyEnchantments.setup();

View file

@ -10,6 +10,7 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.craftbukkit.CraftLootTable;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
@ -97,6 +98,14 @@ public final class DummyServer implements InvocationHandler {
}
}
);
methods.put(Server.class.getMethod("getRegistry", Class.class),
new MethodHandler() {
@Override
public Object handle(DummyServer server, Object[] args) {
return CraftRegistry.createRegistry((Class) args[0], AbstractTestingBase.REGISTRY_CUSTOM);
}
}
);
Bukkit.setServer(Proxy.getProxyClass(Server.class.getClassLoader(), Server.class).asSubclass(Server.class).getConstructor(InvocationHandler.class).newInstance(new DummyServer()));
} catch (Throwable t) {
throw new Error(t);