HyCodeYourTale
classpublicPriority 3

HytaleGenerator

com.hypixel.hytale.builtin.hytalegenerator.plugin.HytaleGenerator

extends JavaPlugin

1

Methods

1

Public Methods

5

Fields

1

Constructors

Constructors

public
HytaleGenerator(JavaPluginInit init)

Methods

Public Methods (1)

public
CompletableFuture<GeneratedChunk> submitChunkRequest(ChunkRequest request)
@Nonnull

Fields

Private/Package Fields (5)

privateAssetManager assetManager
privateRunnable assetReloadListener
privateint concurrency
privateThreadPoolExecutor concurrentExecutor
privateExecutorService mainExecutor

Inheritance

Parent
Current
Interface
Child

Use mouse wheel to zoom, drag to pan. Click nodes to navigate.

Related Classes

Source Code

package com.hypixel.hytale.builtin.hytalegenerator.plugin;

import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil;
import com.hypixel.hytale.builtin.hytalegenerator.PropField;
import com.hypixel.hytale.builtin.hytalegenerator.assets.AssetManager;
import com.hypixel.hytale.builtin.hytalegenerator.assets.SettingsAsset;
import com.hypixel.hytale.builtin.hytalegenerator.assets.worldstructures.WorldStructureAsset;
import com.hypixel.hytale.builtin.hytalegenerator.biome.BiomeType;
import com.hypixel.hytale.builtin.hytalegenerator.biomemap.BiomeMap;
import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkGenerator;
import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkRequest;
import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.FallbackGenerator;
import com.hypixel.hytale.builtin.hytalegenerator.commands.ViewportCommand;
import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache;
import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.NStagedChunkGenerator;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NEntityBuffer;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NBiomeDistanceStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NBiomeStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NEnvironmentStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NPropStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NTerrainStage;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NTintStage;
import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedChunk;
import com.hypixel.hytale.server.core.universe.world.worldgen.provider.IWorldGenProvider;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;

public class HytaleGenerator extends JavaPlugin {
   private AssetManager assetManager;
   private Runnable assetReloadListener;
   private final Map<ChunkRequest.GeneratorProfile, ChunkGenerator> generators = new HashMap<>();
   private final Semaphore chunkGenerationSemaphore = new Semaphore(1);
   private int concurrency;
   private ExecutorService mainExecutor;
   private ThreadPoolExecutor concurrentExecutor;

   @Override
   protected void start() {
      super.start();
      if (this.mainExecutor == null) {
         this.loadExecutors(this.assetManager.getSettingsAsset());
      }

      if (this.assetReloadListener == null) {
         this.assetReloadListener = () -> this.reloadGenerators();
         this.assetManager.registerReloadListener(this.assetReloadListener);
      }
   }

   @Nonnull
   public CompletableFuture<GeneratedChunk> submitChunkRequest(@Nonnull ChunkRequest request) {
      return CompletableFuture.<GeneratedChunk>supplyAsync(() -> {
         GeneratedChunk var3;
         try {
            this.chunkGenerationSemaphore.acquireUninterruptibly();
            ChunkGenerator generator = this.getGenerator(request.generatorProfile());
            var3 = generator.generate(request.arguments());
         } finally {
            this.chunkGenerationSemaphore.release();
         }

         return var3;
      }, this.mainExecutor).handle((r, e) -> {
         if (e == null) {
            return (GeneratedChunk)r;
         } else {
            LoggerUtil.logException("generation of the chunk with request " + request, e, LoggerUtil.getLogger());
            return FallbackGenerator.INSTANCE.generate(request.arguments());
         }
      });
   }

   @Override
   protected void setup() {
      this.assetManager = new AssetManager(this.getEventRegistry(), this.getLogger());
      BuilderCodec<HandleProvider> generatorProvider = BuilderCodec.builder(HandleProvider.class, () -> new HandleProvider(this))
         .documentation("The standard generator for Hytale.")
         .append(new KeyedCodec<>("WorldStructure", Codec.STRING), HandleProvider::setWorldStructureName, HandleProvider::getWorldStructureName)
         .documentation("The world structure to be used for this world.")
         .add()
         .append(new KeyedCodec<>("PlayerSpawn", Transform.CODEC), HandleProvider::setPlayerSpawn, HandleProvider::getPlayerSpawn)
         .add()
         .build();
      IWorldGenProvider.CODEC.register("HytaleGenerator", HandleProvider.class, generatorProvider);
      this.getCommandRegistry().registerCommand(new ViewportCommand(this.assetManager));
   }

   @Nonnull
   public NStagedChunkGenerator createStagedChunkGenerator(
      @Nonnull ChunkRequest.GeneratorProfile generatorProfile, @Nonnull WorldStructureAsset worldStructureAsset, @Nonnull SettingsAsset settingsAsset
   ) {
      WorkerIndexer workerIndexer = new WorkerIndexer(this.concurrency);
      SeedBox seed = new SeedBox(generatorProfile.seed());
      MaterialCache materialCache = new MaterialCache();
      BiomeMap<SolidMaterial> biomeMap = worldStructureAsset.buildBiomeMap(new WorldStructureAsset.Argument(materialCache, seed, workerIndexer));
      worldStructureAsset.cleanUp();
      NStagedChunkGenerator.Builder generatorBuilder = new NStagedChunkGenerator.Builder();
      List<BiomeType> allBiomes = biomeMap.allPossibleValues();
      List<Integer> allRuntimes = new ArrayList<>(getAllPossibleRuntimeIndices(allBiomes));
      allRuntimes.sort(Comparator.naturalOrder());
      int bufferTypeIndexCounter = 0;
      NParametrizedBufferType biome_bufferType = new NParametrizedBufferType(
         "Biome", bufferTypeIndexCounter++, NBiomeStage.bufferClass, NBiomeStage.biomeTypeClass, () -> new NCountedPixelBuffer<>(NBiomeStage.biomeTypeClass)
      );
      NStage biomeStage = new NBiomeStage("BiomeStage", biome_bufferType, biomeMap);
      generatorBuilder.appendStage(biomeStage);
      NParametrizedBufferType biomeDistance_bufferType = new NParametrizedBufferType(
         "BiomeDistance",
         bufferTypeIndexCounter++,
         NBiomeDistanceStage.biomeDistanceBufferClass,
         NBiomeDistanceStage.biomeDistanceClass,
         () -> new NSimplePixelBuffer<>(NBiomeDistanceStage.biomeDistanceClass)
      );
      int MAX_BIOME_DISTANCE_RADIUS = 512;
      int interpolationRadius = Math.clamp((long)(worldStructureAsset.getBiomeTransitionDistance() / 2), 0, 512);
      int biomeEdgeRadius = Math.clamp((long)worldStructureAsset.getMaxBiomeEdgeDistance(), 0, 512);
      int maxDistance = Math.max(interpolationRadius, biomeEdgeRadius);
      NStage biomeDistanceStage = new NBiomeDistanceStage("BiomeDistanceStage", biome_bufferType, biomeDistance_bufferType, (double)maxDistance);
      generatorBuilder.appendStage(biomeDistanceStage);
      int materialBufferIndexCounter = 0;
      NParametrizedBufferType material0_bufferType = generatorBuilder.MATERIAL_OUTPUT_BUFFER_TYPE;
      if (!allRuntimes.isEmpty()) {
         material0_bufferType = new NParametrizedBufferType(
            "Material" + materialBufferIndexCounter,
            bufferTypeIndexCounter++,
            NTerrainStage.materialBufferClass,
            NTerrainStage.materialClass,
            () -> new NVoxelBuffer<>(NTerrainStage.materialClass)
         );
         materialBufferIndexCounter++;
      }

      NStage terrainStage = new NTerrainStage(
         "TerrainStage", biome_bufferType, biomeDistance_bufferType, material0_bufferType, interpolationRadius, materialCache, workerIndexer
      );
      generatorBuilder.appendStage(terrainStage);
      NParametrizedBufferType materialInput_bufferType = material0_bufferType;
      NBufferType entityInput_bufferType = null;

      for (int i = 0; i < allRuntimes.size() - 1; i++) {
         int runtime = allRuntimes.get(i);
         String runtimeString = Integer.toString(runtime);
         NParametrizedBufferType materialOutput_bufferType = new NParametrizedBufferType(
            "Material" + materialBufferIndexCounter,
            bufferTypeIndexCounter++,
            NTerrainStage.materialBufferClass,
            NTerrainStage.materialClass,
            () -> new NVoxelBuffer<>(NTerrainStage.materialClass)
         );
         NBufferType entityOutput_bufferType = new NBufferType(
            "Entity" + materialBufferIndexCounter, bufferTypeIndexCounter++, NEntityBuffer.class, NEntityBuffer::new
         );
         NStage propStage = new NPropStage(
            "PropStage" + runtimeString,
            biome_bufferType,
            biomeDistance_bufferType,
            materialInput_bufferType,
            entityInput_bufferType,
            materialOutput_bufferType,
            entityOutput_bufferType,
            materialCache,
            allBiomes,
            runtime
         );
         generatorBuilder.appendStage(propStage);
         materialInput_bufferType = materialOutput_bufferType;
         entityInput_bufferType = entityOutput_bufferType;
         materialBufferIndexCounter++;
      }

      if (!allRuntimes.isEmpty()) {
         int runtime = allRuntimes.getLast();
         String runtimeString = Integer.toString(runtime);
         NStage propStage = new NPropStage(
            "PropStage" + runtimeString,
            biome_bufferType,
            biomeDistance_bufferType,
            materialInput_bufferType,
            entityInput_bufferType,
            generatorBuilder.MATERIAL_OUTPUT_BUFFER_TYPE,
            generatorBuilder.ENTITY_OUTPUT_BUFFER_TYPE,
            materialCache,
            allBiomes,
            runtime
         );
         generatorBuilder.appendStage(propStage);
      }

      NStage tintStage = new NTintStage("TintStage", biome_bufferType, generatorBuilder.TINT_OUTPUT_BUFFER_TYPE);
      generatorBuilder.appendStage(tintStage);
      NStage environmentStage = new NEnvironmentStage("EnvironmentStage", biome_bufferType, generatorBuilder.ENVIRONMENT_OUTPUT_BUFFER_TYPE);
      generatorBuilder.appendStage(environmentStage);
      double bufferCapacityFactor = Math.max(0.0, settingsAsset.getBufferCapacityFactor());
      double targetViewDistance = Math.max(0.0, settingsAsset.getTargetViewDistance());
      double targetPlayerCount = Math.max(0.0, settingsAsset.getTargetPlayerCount());
      Set<Integer> statsCheckpoints = new HashSet<>(settingsAsset.getStatsCheckpoints());
      return generatorBuilder.withStats("WorldStructure Name: " + generatorProfile.worldStructureName(), statsCheckpoints)
         .withMaterialCache(materialCache)
         .withConcurrentExecutor(this.concurrentExecutor, workerIndexer)
         .withBufferCapacity(bufferCapacityFactor, targetViewDistance, targetPlayerCount)
         .build();
   }

   @Nonnull
   private static Set<Integer> getAllPossibleRuntimeIndices(@Nonnull List<BiomeType> biomes) {
      Set<Integer> allRuntimes = new HashSet<>();

      for (BiomeType biome : biomes) {
         for (PropField propField : biome.getPropFields()) {
            allRuntimes.add(propField.getRuntime());
         }
      }

      return allRuntimes;
   }

   @Nonnull
   private ChunkGenerator getGenerator(@Nonnull ChunkRequest.GeneratorProfile profile) {
      ChunkGenerator generator = this.generators.get(profile);
      if (generator == null) {
         if (profile.worldStructureName() == null) {
            LoggerUtil.getLogger().warning("World Structure asset not loaded.");
            return FallbackGenerator.INSTANCE;
         }

         WorldStructureAsset worldStructureAsset = this.assetManager.getWorldStructureAsset(profile.worldStructureName());
         if (worldStructureAsset == null) {
            LoggerUtil.getLogger().warning("World Structure asset not found: " + profile.worldStructureName());
            return FallbackGenerator.INSTANCE;
         }

         SettingsAsset settingsAsset = this.assetManager.getSettingsAsset();
         if (settingsAsset == null) {
            LoggerUtil.getLogger().warning("Settings asset not found.");
            return FallbackGenerator.INSTANCE;
         }

         generator = this.createStagedChunkGenerator(profile, worldStructureAsset, settingsAsset);
         this.generators.put(profile, generator);
      }

      return generator;
   }

   private void loadExecutors(@Nonnull SettingsAsset settingsAsset) {
      int newConcurrency = getConcurrency(settingsAsset);
      if (newConcurrency != this.concurrency || this.mainExecutor == null || this.concurrentExecutor == null) {
         this.concurrency = newConcurrency;
         if (this.mainExecutor == null) {
            this.mainExecutor = Executors.newSingleThreadExecutor();
         }

         if (this.concurrentExecutor != null && !this.concurrentExecutor.isShutdown()) {
            try {
               this.concurrentExecutor.shutdown();
               if (!this.concurrentExecutor.awaitTermination(1L, TimeUnit.MINUTES)) {
               }
            } catch (InterruptedException var4) {
               throw new RuntimeException(var4);
            }
         }

         this.concurrentExecutor = new ThreadPoolExecutor(this.concurrency, this.concurrency, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> {
            Thread t = new Thread(r, "HytaleGenerator-Worker");
            t.setPriority(1);
            t.setDaemon(true);
            return t;
         });
         if (this.mainExecutor == null || this.mainExecutor.isShutdown()) {
            this.mainExecutor = Executors.newSingleThreadExecutor();
         }
      }
   }

   private static int getConcurrency(@Nonnull SettingsAsset settingsAsset) {
      int concurrencySetting = settingsAsset.getCustomConcurrency();
      int availableProcessors = Runtime.getRuntime().availableProcessors();
      int value = 1;
      if (concurrencySetting < 1) {
         value = Math.max(availableProcessors, 1);
      } else {
         if (concurrencySetting > availableProcessors) {
            LoggerUtil.getLogger().warning("Concurrency setting " + concurrencySetting + " exceeds available processors " + availableProcessors);
         }

         value = concurrencySetting;
      }

      return value;
   }

   private void reloadGenerators() {
      try {
         this.chunkGenerationSemaphore.acquireUninterruptibly();
         this.loadExecutors(this.assetManager.getSettingsAsset());
         this.generators.clear();
      } finally {
         this.chunkGenerationSemaphore.release();
      }

      LoggerUtil.getLogger().info("Reloaded HytaleGenerator.");
   }

   public HytaleGenerator(@Nonnull JavaPluginInit init) {
      super(init);
   }
}