Speed up generation

This commit is contained in:
samczsun 2017-02-19 23:53:19 -05:00 committed by cnr
parent f8dbbce106
commit b782f5ced9
2 changed files with 248 additions and 229 deletions

View File

@ -1,10 +1,7 @@
package nautilus.game.arcade.uhc;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@ -13,7 +10,6 @@ import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import net.minecraft.server.v1_8_R3.BiomeBase;
@ -27,26 +23,35 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Difficulty;
import org.bukkit.World;
import org.bukkit.WorldBorder;
import org.bukkit.WorldCreator;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.spigotmc.WatchdogThread;
import org.zeroturnaround.zip.ByteSource;
import org.zeroturnaround.zip.FileSource;
import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class WorldGen extends JavaPlugin
public class WorldGen extends JavaPlugin implements Listener
{
private static final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);
private static final int MIN_X = -1000;
private static final int MIN_Z = -1000;
private static final int MAX_X = 1000;
private static final int MAX_Z = 1000;
// The world will be -MAP_SIZE to MAP_SIZE large
private static final int MAP_SIZE = 1000;
private static final int VIEW_DISTANCE = 5;
private static final String API_HOST_FILE = "api-config.dat";
@ -78,264 +83,276 @@ public class WorldGen extends JavaPlugin
}
}
@EventHandler
public void login(AsyncPlayerPreLoginEvent event)
{
event.setKickMessage("get out");
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
@EventHandler
public void unload(ChunkUnloadEvent event)
{
event.setCancelled(true);
}
@EventHandler
public void init(WorldInitEvent event)
{
// Prevent any eager generation
event.getWorld().setKeepSpawnInMemory(false);
}
@Override
public void onEnable()
{
getLogger().info("Cleaning up other worlds");
for (World world : getServer().getWorlds())
{
world.setKeepSpawnInMemory(false);
world.setSpawnFlags(false, false);
world.setAmbientSpawnLimit(0);
world.setAnimalSpawnLimit(0);
world.setMonsterSpawnLimit(0);
world.setWaterAnimalSpawnLimit(0);
world.getEntities().forEach(Entity::remove);
for (Chunk chunk : world.getLoadedChunks())
{
chunk.unload(false, false);
}
getServer().unloadWorld(world, false);
getLogger().info("Unloaded " + world.getName());
}
getLogger().info("Replacing biomes");
BiomeBase.getBiomes()[BiomeBase.OCEAN.id] = BiomeBase.PLAINS;
BiomeBase.getBiomes()[BiomeBase.DEEP_OCEAN.id] = BiomeBase.PLAINS;
BiomeBase.getBiomes()[BiomeBase.SWAMPLAND.id] = BiomeBase.PLAINS;
BiomeBase.getBiomes()[BiomeBase.RIVER.id] = BiomeBase.PLAINS;
getLogger().info("Forcing system GC");
System.gc();
WatchdogThread.doStop();
getServer().getPluginManager().registerEvents(this, this);
File root = new File(".");
if (!root.exists())
{
getLogger().severe("Root folder does not exist. Aborting");
System.exit(0);
return;
}
File outputDirectory = new File(root, "output");
if (!outputDirectory.exists())
{
if (!outputDirectory.mkdir())
{
getLogger().severe("Could not create output folder. Aborting");
System.exit(0);
return;
}
}
long seed = ThreadLocalRandom.current().nextLong();
File outputFile = new File(outputDirectory, "UHC_Map" + seed + ".zip");
if (outputFile.exists())
{
getLogger().info("Seed " + seed + " has already been generated. Skipping");
System.exit(0);
return;
}
try
{
File root = new File(".");
if (!root.exists())
if (!outputFile.createNewFile())
{
getLogger().severe("Root folder does not exist. Aborting");
getLogger().severe("Could not create new output file. Aborting");
System.exit(0);
return;
}
}
catch (IOException e)
{
getLogger().log(Level.SEVERE, "Could not create new output file. Aborting", e);
System.exit(0);
return;
}
File outputDirectory = new File(root, "output");
if (!outputDirectory.exists())
File previousSession = new File("generating");
if (previousSession.exists())
{
if (!FileUtils.deleteQuietly(previousSession))
{
if (!outputDirectory.mkdir())
{
getLogger().severe("Could not create output folder. Aborting");
System.exit(0);
return;
}
}
long seed = ThreadLocalRandom.current().nextLong();
File outputFile = new File(outputDirectory, "UHC_Map" + seed + ".zip");
if (outputFile.exists())
{
getLogger().info("Seed " + seed + " has already been generated. Skipping");
getLogger().severe("Could not delete previous generation session. Aborting");
System.exit(0);
return;
}
}
try
getLogger().info("Generating world seed " + seed);
World world = new WorldCreator("generating")
.environment(World.Environment.NORMAL)
.seed(seed)
.createWorld();
world.setKeepSpawnInMemory(false);
world.setDifficulty(Difficulty.HARD);
WorldBorder border = world.getWorldBorder();
border.setCenter(0.0, 0.0);
border.setSize(MAP_SIZE * 2);
int minChunkX = (-MAP_SIZE >> 4) - VIEW_DISTANCE;
int minChunkZ = (-MAP_SIZE >> 4) - VIEW_DISTANCE;
int maxChunkX = (MAP_SIZE >> 4) + VIEW_DISTANCE;
int maxChunkZ = (MAP_SIZE >> 4) + VIEW_DISTANCE;
net.minecraft.server.v1_8_R3.WorldServer nmsWorld = ((CraftWorld) world).getHandle();
//
// Field mfield = nmsWorld.getClass().getDeclaredField("M");
// mfield.setAccessible(true);
//
// HashTreeSet<NextTickListEntry> treeSet = ((HashTreeSet) mfield.get(nmsWorld));
for (int x = minChunkX; x <= maxChunkX; x++)
{
getLogger().info("Generating x coord " + x);
for (int z = minChunkZ; z <= maxChunkZ; z++)
{
if (!outputFile.createNewFile())
{
getLogger().severe("Could not create new output file. Aborting");
System.exit(0);
return;
}
}
catch (IOException e)
{
getLogger().log(Level.SEVERE, "Could not create new output file. Aborting", e);
System.exit(0);
return;
world.getChunkAt(x, z).load(true);
nmsWorld.a(true);
// Manually tick blocks - this should be the equivalent of letting a full server tick run once
// between each chunk generation, except we cut out the extra useless stuff
}
getLogger().info("Generating world seed " + seed);
// System.out.println("M: " + treeSet.size());
// System.out.println("E: " + nmsWorld.entityList.size());
// System.out.println("TE: " + nmsWorld.tileEntityList.size());
// System.out.println("C: " + nmsWorld.chunkProviderServer.chunks.size());
}
World world = new WorldCreator("generating")
.environment(World.Environment.NORMAL)
.seed(seed)
.createWorld();
world.setDifficulty(Difficulty.HARD);
world.setKeepSpawnInMemory(false);
int minChunkX = (MIN_X >> 4) - VIEW_DISTANCE;
int minChunkZ = (MIN_Z >> 4) - VIEW_DISTANCE;
int maxChunkX = (MAX_X >> 4) + VIEW_DISTANCE;
int maxChunkZ = (MAX_Z >> 4) + VIEW_DISTANCE;
for (int x = minChunkX; x <= maxChunkX; x++)
for (int x = minChunkX; x <= maxChunkX; x++)
{
getLogger().info("Unloading x coord " + x);
for (int z = minChunkZ; z <= maxChunkZ; z++)
{
getLogger().info("Generating x coord " + x);
for (int z = minChunkZ; z <= maxChunkZ; z++)
{
world.getChunkAt(x, z).load(true);
}
world.getChunkAt(x, z).unload(true, false);
}
for (int x = minChunkX; x <= maxChunkX; x++)
// System.out.println("M: " + treeSet.size());
// System.out.println("E: " + nmsWorld.entityList.size());
// System.out.println("TE: " + nmsWorld.tileEntityList.size());
// System.out.println("C: " + nmsWorld.chunkProviderServer.chunks.size());
}
getLogger().info("Unloading and saving world");
Bukkit.unloadWorld(world, true);
getLogger().info("Finished unloading and saving world");
StringBuilder worldconfig = new StringBuilder();
worldconfig.append("MAP_NAME:UHC World").append(System.lineSeparator());
worldconfig.append("MAP_AUTHOR:Mineplex").append(System.lineSeparator());
worldconfig.append("MIN_X:").append(-MAP_SIZE).append(System.lineSeparator());
worldconfig.append("MIN_Z:").append(-MAP_SIZE).append(System.lineSeparator());
worldconfig.append("MAX_X:").append(MAP_SIZE).append(System.lineSeparator());
worldconfig.append("MAX_Z:").append(MAP_SIZE).append(System.lineSeparator());
for (int i = 1; i <= 60; i++)
{
worldconfig.append("TEAM_NAME:").append(i).append(System.lineSeparator());
worldconfig.append("TEAM_SPAWNS:0,0,0").append(System.lineSeparator());
}
File worldFolder = new File(root, "generating");
File regionFolder = new File(worldFolder, "region");
File[] regionFiles = regionFolder.listFiles();
if (regionFiles == null)
{
getLogger().severe("Unexpected null region files. Aborting");
System.exit(0);
return;
}
List<ZipEntrySource> zipEntrySourceList = new ArrayList<>();
zipEntrySourceList.add(new ByteSource("WorldConfig.dat", worldconfig.toString().getBytes(StandardCharsets.UTF_8)));
for (File file : regionFiles)
{
zipEntrySourceList.add(new FileSource("region/" + file.getName(), file));
}
zipEntrySourceList.add(new FileSource("level.dat", new File(worldFolder, "level.dat")));
ZipUtil.pack(zipEntrySourceList.toArray(new ZipEntrySource[zipEntrySourceList.size()]), outputFile);
FileUtils.deleteQuietly(worldFolder);
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.build();
HttpPost request = new HttpPost("http://" + API_HOST_MAP.get("ENDERCHEST") + "/map/uhc/upload");
request.addHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", outputFile.getName());
jsonObject.addProperty("location", outputFile.toURI().toString());
request.setEntity(new StringEntity(new Gson().toJson(jsonObject), StandardCharsets.UTF_8));
try
{
getLogger().info("Uploading " + seed + "!");
HttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() != 200)
{
getLogger().info("Unloading x coord " + x);
for (int z = minChunkZ; z <= maxChunkZ; z++)
if (response.getStatusLine().getStatusCode() == 409)
{
world.getChunkAt(x, z).unload(true, false);
}
}
getLogger().info("Unloading and saving world");
Bukkit.unloadWorld(world, true);
getLogger().info("Finished unloading and saving world");
StringBuilder worldconfig = new StringBuilder();
worldconfig.append("MAP_NAME:UHC World").append(System.lineSeparator());
worldconfig.append("MAP_AUTHOR:Mineplex").append(System.lineSeparator());
worldconfig.append("MIN_X:").append(MIN_X).append(System.lineSeparator());
worldconfig.append("MIN_Z:").append(MIN_Z).append(System.lineSeparator());
worldconfig.append("MAX_X:").append(MAX_X).append(System.lineSeparator());
worldconfig.append("MAX_Z:").append(MAX_Z).append(System.lineSeparator());
for (int i = 1; i <= 60; i++)
{
worldconfig.append("TEAM_NAME:").append(i).append(System.lineSeparator());
worldconfig.append("TEAM_SPAWNS:0,0,0").append(System.lineSeparator());
}
File worldFolder = new File(root, "generating");
File regionFolder = new File(worldFolder, "region");
File[] regionFiles = regionFolder.listFiles();
if (regionFiles == null)
{
getLogger().severe("Unexpected null region files. Aborting");
System.exit(0);
return;
}
List<ZipEntrySource> zipEntrySourceList = new ArrayList<>();
zipEntrySourceList.add(new ZipEntrySource()
{
@Override
public String getPath()
{
return "WorldConfig.dat";
}
@Override
public ZipEntry getEntry()
{
return new ZipEntry(getPath());
}
@Override
public InputStream getInputStream() throws IOException
{
return new ByteArrayInputStream(worldconfig.toString().getBytes(StandardCharsets.UTF_8));
}
});
for (File file : regionFiles)
{
zipEntrySourceList.add(new ZipEntrySource()
{
@Override
public String getPath()
{
return "region/" + file.getName();
}
@Override
public ZipEntry getEntry()
{
return new ZipEntry(getPath());
}
@Override
public InputStream getInputStream() throws IOException
{
return new FileInputStream(file);
}
});
}
zipEntrySourceList.add(new ZipEntrySource()
{
@Override
public String getPath()
{
return "level.dat";
}
@Override
public ZipEntry getEntry()
{
return new ZipEntry(getPath());
}
@Override
public InputStream getInputStream() throws IOException
{
return new FileInputStream(new File(worldFolder, "level.dat"));
}
});
ZipUtil.pack(zipEntrySourceList.toArray(new ZipEntrySource[zipEntrySourceList.size()]), outputFile);
FileUtils.deleteQuietly(worldFolder);
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.build();
HttpPost request = new HttpPost("http://" + API_HOST_MAP.get("ENDERCHEST") + "/map/uhc/upload");
request.addHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", outputFile.getName());
jsonObject.addProperty("location", outputFile.toURI().toString());
request.setEntity(new StringEntity(new Gson().toJson(jsonObject), StandardCharsets.UTF_8));
try
{
getLogger().info("Uploading " + seed + "!");
HttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() != 200)
{
if (response.getStatusLine().getStatusCode() == 409)
{
getLogger().warning("Oops - Server rejected " + seed + " because it was already generated");
if (!outputFile.delete())
{
getLogger().warning("Could not clean up " + seed);
}
}
else
{
getLogger().severe("Failed to upload " + seed + ": " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase());
}
}
else
{
getLogger().info("Uploaded " + seed + "!");
getLogger().warning("Oops - Server rejected " + seed + " because it was already generated");
if (!outputFile.delete())
{
getLogger().warning("Could not clean up " + seed);
}
}
else
{
getLogger().severe("Failed to upload " + seed + ": " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase());
}
}
catch (IOException e)
else
{
e.printStackTrace();
}
finally
{
getLogger().info("Finished generating world seed " + seed);
getLogger().info("Uploaded " + seed + "!");
if (!outputFile.delete())
{
getLogger().warning("Could not clean up " + seed);
}
}
}
catch (Throwable t)
catch (IOException e)
{
t.printStackTrace();
getLogger().log(Level.SEVERE, "An error occurred while uploading " + seed + "!", e);
}
System.exit(0);
finally
{
getLogger().info("Finished generating world seed " + seed);
}
Bukkit.shutdown();
}
}

View File

@ -365,17 +365,19 @@ public abstract class UHC extends Game
UtilPlayer.message(caller, F.main("Debug", "World info for " + targetWorld.getName()));
UtilPlayer.message(caller, F.desc("Chunks", String.valueOf(chunks.length)));
UtilPlayer.message(caller, F.desc("Entities", String.valueOf(targetWorld.getEntities().size())));
UtilPlayer.message(caller, F.desc("Tile Entities", String.valueOf(Arrays.stream(chunks).map(Chunk::getTileEntities).map(Arrays::asList).flatMap(Collection::stream)
.count())));
UtilPlayer.message(caller, F.desc("Tile Entities", String.valueOf(Arrays.stream(chunks).map(Chunk::getTileEntities).map(Arrays::asList).mapToLong(Collection::size).sum())));
UtilPlayer.message(caller, F.desc("View Distance", String.valueOf(nmsWorld.spigotConfig.viewDistance)));
UtilPlayer.message(caller, F.desc("Unload queue size", String.valueOf(nmsWorld.chunkProviderServer.unloadQueue.size())));
try
{
HashTreeSet<NextTickListEntry> m = (HashTreeSet<NextTickListEntry>) nmsWorld.getClass().getField("M").get(nmsWorld);
Field f = nmsWorld.getClass().getDeclaredField("M");
f.setAccessible(true);
HashTreeSet<NextTickListEntry> m = (HashTreeSet<NextTickListEntry>) f.get(nmsWorld);
UtilPlayer.message(caller, F.desc("Pending tick", String.valueOf(m.size())));
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e)
catch (ReflectiveOperationException e)
{
e.printStackTrace();
}
@ -571,7 +573,7 @@ public abstract class UHC extends Game
}
}
});
registerDebugCommand(new DebugCommand("uhccallchunks", Rank.DEVELOPER)
registerDebugCommand(new DebugCommand("uhcallchunks", Rank.DEVELOPER)
{
@Override