Solved Creating custom BiomeGrid

Discussion in 'Plugin Development' started by The_Spaceman, Sep 15, 2018.

  1. Offline

    The_Spaceman

    I want to make a custom BiomeGrid (it doesn't have to be a BiomeGrid object)

    it has to be fully customiable to create, like the amount of biomes and size per biome.
    eventually I am replacing the Biome with something else, but this is easier to explain and test with.

    The problem is that I have no idea to create someting like this, and the Minecraft version is not really understandable to me...
    Code:
    //in the class net.minecraft.server.<version>.GenLayer
    
    //var1 chunkX
    //var2 chunkZ
    //var3 16 (chunk width)
    //var4 16 (chunk height)
    public BiomeBase[] a(int var1, int var2, int var3, int var4, @Nullable BiomeBase var5) {
        AreaDimension var6 = new AreaDimension(var1, var2, var3, var4);
        AreaLazy var7 = (AreaLazy)this.a.make(var6);
        BiomeBase[] var8 = new BiomeBase[var3 * var4];
        for(int var9 = 0; var9 < var4; ++var9) {
            for(int var10 = 0; var10 < var3; ++var10) {
                var8[var10 + var9 * var3] = BiomeBase.getBiome(var7.a(var10, var9), var5);
            }
        }
        return var8;
    }
    
    but this is not all of it, just some...

    So I nead a custom one, where I can create a BiomeGrid with custom 'biomes'

    I hope someone can help me.

    new:

    anyone? I'm still stuck on this...
     
    Last edited: Sep 24, 2018
  2. Offline

    The_Spaceman

  3. Offline

    knokko

    @The_Spaceman

    I think I know a way to do this, but it will be quite complex:

    You can create a class that extends ChunkGenerator and register that in your plug-in.
    In that class, override ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) and use the methods of 'biome' to set the biome of each individual (x,z) pair in every chunk.

    To register it, add this to your plugin class:

    @Override
    public ChunkGenerator getDefaultWorldGenerator(String worldName, String id){
    return new CustomGenerator();
    }

    And this to your plugin.yml
    load: STARTUP

    I hope this points you in the right direction.
     
  4. Offline

    The_Spaceman

    thanks for the answer, but that is not what I want... I already know how to make a custom world, but I want to create my own layout of the biomes. something like:
    CustomBiome getBiomeFromXZ(long seed, int x, int z, List<CustomBiome> customBiomes);

    and if possble make the CustomBiome configurable, like biome size and how rare the biome will be.
    I hope you understand my problem better now.
     
  5. Offline

    knokko

    @The_Spaceman

    I think I already know what you are trying to do. The method I made up is really complex for this, but it might work. My idea was to use the BiomeGrid parameter in the generateChunkData to manually set the biome for every (x, z) pair.
    After that, there is hopefully a way to get the vanilla generator for each biome and call some kind of method of the right generator and hope that it will create the layout.

    If anyone else finds a better approach, you should use that, but I'm afraid there are no more ideas than this horrible one.
     
  6. Offline

    The_Spaceman

    I don't really need to set the biome (sort of), just do something else in each biome. I need it to create custom worlds (custom world generating already works) with each biome that has his own 'generator'. That's why I want it to make it that I can specify the amount of 'biomes' and size
    https://dev.bukkit.org/projects/terraingenerator

    [​IMG]
    source: Simon Tatham's Puzzels -> map

    If its like this (but more smooth), with only colors I can give each color a 'biome' and work with that...

    I was thinking, what if I plot some random points on a map and blow up the points (like a balloon). But the problems are:
    1: I've to save the whole map, or recalculate the grid when I need it.
    2: I have no idea of how to approach it, like inflate the 'balloons' and make them not overwrite other 'balloons'. I've never made anything like this and I don't know the physics.

    But I want to thank you anyway for your effort in helping me so far :D
     
  7. Offline

    knokko

    @The_Spaceman

    So if I understand it correctly:
    You have most things ready already, but you only need a way to determine what biome you want to place where?
     
  8. Offline

    The_Spaceman

    the 'biome' placement can be random, but I want to control the amount of biomes and size of each biome.
    And if its possible to edit the biome layout of the world at any time that would be perfect, that is why I was thinking like a method:
    CustomBiome getBiomeFromXZ(long seed, int x, int z, List<CustomBiome> customBiomes) {
    //some difficult math...
    return someCustomBiome;
    }
     
  9. Offline

    knokko

    @The_Spaceman

    I think I know some kind of mathematics to accomplish this:

    Divide the world in sections of for instance 5000 x 5000. That example size might seem big, but you won't actually generate the entire section, just the points of your biome layout, which is much less work than the complete world gen.

    Then make sure every section has it's own seed that can be calculated by the sectionX, sectionZ and the world seed.
    Then use the section seed to create an instance of Random.
    With the random, you generate an amount of points in the section where every point has a random x, z and custom biome (and possibly more properties that can be used in the formula in the next paragraph).

    Once the points are set, you will need to use some formula to determine the custom biome for the requested x and z. (The easiest would be to simple use the closest point and find that by Pythagoras (or whatever his english name is).)
    For the x and z coords that are near the edge of a section, you might need to calculate the neighbour section as well.

    All coordinates will always use the same section because Random is a pseudo random number generator which means that 2 randoms with the same seed will do the same in the same method.
     
  10. Offline

    The_Spaceman

    Code:java
    1. public static String getBiome(int x, int z, long seed) {
    2.  
    3. //create points
    4. Random random = new Random(seed + (x*5000) * (z*5000) + (z*5000));
    5. ArrayList<Dimension> points = new ArrayList<>();
    6. for (int i = 0; i < 100; i++) {
    7. points.add(new Dimension(random.nextInt(5000), random.nextInt(5000)));
    8. }
    9.  
    10. //find closest point
    11. double closest = Double.MAX_VALUE;
    12. Dimension chosenPoint = new Dimension(Integer.MIN_VALUE, Integer.MAX_VALUE);
    13. for (Dimension d : points) {
    14. double distance = Math.sqrt(NumberConversions.square(d.getWidth() - x) + NumberConversions.square(d.getHeight() - z));
    15. if (distance < closest) {
    16. closest = distance;
    17. chosenPoint = d;
    18. }
    19. }
    20.  
    21. //get biome and return
    22. List<String> biomeList = Arrays.asList("biome1", "biome2");
    23. return biomeList.get( new Random((long) (seed + chosenPoint.getHeight() * chosenPoint.width + chosenPoint.width)).nextInt(biomeList.size()));
    24. }

    did you mean anything like this? I haven't tested it yet

    update:
    Show Spoiler
    Code:java
    1. public static void getBiome(long seed) {
    2. int x = 1000;
    3. int z = 1000;
    4.  
    5. //create points
    6. Random random = new Random(seed + (x) * (z) + (z));
    7. ArrayList<Dimension> points = new ArrayList<>();
    8. for (int i = 0; i < 200; i++) {
    9. points.add(new Dimension(random.nextInt(x), random.nextInt(z)));
    10. }
    11.  
    12. BufferedImage image = new BufferedImage(x, z, BufferedImage.TYPE_INT_RGB);
    13. for (int i = 0; i < x; i++) {
    14. for (int j = 0; j < z; j++) {
    15. //find closest point
    16. double closest = Double.MAX_VALUE;
    17. Dimension chosenPoint = new Dimension(Integer.MIN_VALUE, Integer.MAX_VALUE);
    18. for (Dimension d : points) {
    19. double distance = Math.sqrt(NumberConversions.square(d.getWidth() - i) + NumberConversions.square(d.getHeight() - j));
    20. if (distance < closest) {
    21. closest = distance;
    22. chosenPoint = d;
    23. }
    24. }
    25.  
    26. //get biome and return
    27. List<Color> colorList = Arrays.asList(Color.GREEN, Color.RED, Color.BLUE, Color.YELLOW, Color.GRAY, Color.PINK, Color.CYAN, Color.MAGENTA);
    28. image.setRGB(i, j, colorList.get(new Random((long) (seed + chosenPoint.getHeight() * chosenPoint.width + chosenPoint.width)).nextInt(colorList.size())).getRGB());
    29.  
    30. }
    31. }
    32.  
    33. for (Dimension d : points) {
    34. image.setRGB(d.width, d.height, 0);
    35. }
    36.  
    37. try {
    38. ImageIO.write(image, "png", new File("some file"));
    39. } catch (IOException e) {
    40. e.printStackTrace();
    41. }
    42. }

    and this outputs this file:
    file.png

    but now the question is: how do I make it that I can give a location and it returns a color. but now it just chances the whole picture...
     
    Last edited: Nov 3, 2018
  11. Offline

    knokko

    @The_Spaceman

    Your method is not exactly what I had in mind, but it is close
    -I meant that you should assign a random biome to every point and return the biome of the closest point. Even though you came up with another method, the random part at the end works as well, so don't worry about this.
    -You should make a clear difference between x and sectionX:

    // Nice mathematics
    int sectionX = x / SECTION_SIZE;
    //integer division rounds towards 0, but you should round towards negative infinity, or you will get strange things close // to the origin
    if (x < 0) sectionX--;
    //get x in the range [0, SECTION_SIZE]
    x -= sectionX * SECTION_SIZE;

    The sectionX (and sectionZ) should be used to determine the seed.
    The x and z should be used to determine what the closest point is.

    And you should be able to get the color of a given location by simple calling getBiome(x, z, world.getSeed())
     
    Last edited: Nov 3, 2018
  12. Offline

    The_Spaceman

    I've got it somewhat working :D
    but when I get to the border of the section it wont align with the previous one...
    in the mater of facts, it repeats it self... and using another Random object for the point won't work...
    how would I fix this? the colors still change tho.
    And is there a way to smooth out the lines? now there so straight...
    test1.png
    Show Spoiler

    int size = 1000;

    int sectionX = x / size;
    if (x < 0) sectionX--;
    x -= sectionX * size;

    int sectionZ = z / size;
    if (z < 0) sectionZ--;
    z -= sectionZ * size;

    //create points
    Random random = new Random(seed + (sectionX) * (sectionZ) + (sectionZ));
    ArrayList<Dimension> points = new ArrayList<>();
    for (int i = 0; i < 200; i++) {
    points.add(new Dimension(random.nextInt(size) + sectionX, random.nextInt(size) + sectionZ));
    }
    //find closest point
    double closest = Double.MAX_VALUE;
    Dimension chosenPoint = new Dimension(Integer.MIN_VALUE, Integer.MAX_VALUE);
    for (Dimension d : points) {
    double distance = Math.sqrt(NumberConversions.square(d.getWidth() - x) + NumberConversions.square(d.getHeight() - z));
    if (distance < closest) {
    closest = distance;
    chosenPoint = d;
    }
    }

    //get biome and return
    List<Color> colorList = Arrays.asList(Color.GREEN, Color.RED, Color.BLUE, Color.YELLOW, Color.PINK, Color.CYAN, Color.MAGENTA);
    return colorList.get(new Random((long) (seed + chosenPoint.getHeight() * chosenPoint.width + chosenPoint.width)).nextInt(colorList.size()));
     
    Last edited: Nov 3, 2018
  13. Offline

    knokko

    @The_Spaceman

    I will tell you a few things already, the rest will come later:

    1. Because you are using sectionX and sectionZ a little different than I had in mind, you should remove the lines x -= sectionX * size; and z -= sectionZ * size;

    2. If you want to get rid of the straight lines, you will need to use another formula to determine what point will be chosen. Please create a simple class Point that has:

    int x;
    int z;
    Color color; (later change to biome)
    Extra variables for better formulas, for instance :
    double weight;

    Now you could replace 'double distance = Math.sqrt(NumberConversions.square(d.getWidth() - x) + NumberConversions.square(d.getHeight() - z));' by 'double distance = Math.sqrt(NumberConversions.square(d.getWidth() - x) + NumberConversions.square(d.getHeight() - z)) * d.weight;'
    You could make weight a double between 0.3 and 3.0 (mess around with this yourself later)

    3. In order to avoid the strange things at the end of the sections, you need to add the points of all neighbours of the current region to the list as well.

    4. I don't like your ' new Random(seed + (sectionX) * (sectionZ) + (sectionZ));' because the sectionX will not matter when sectionZ is 0 (this will cause some kind of repetition on the locations where sectionZ is 0)

    5. I can't find any repetition in the pictures you sent, where should I look?


    By the way, I like it that we have a topic with some real algorithmic challenge in the Bukkit forums :D
     
  14. Offline

    The_Spaceman

    I've got it working :D

    here is your repeating pattern...
    test1.png

    but here is the final example:
    test1.png
    I had a lot of problems of checking with the neighbor sections (java run out of memory...), but when I changed the code a bit it worked like a charm

    here is the code: (pls don't mind some of the weird names, and strange way around of working, it was just made to make it work, I now starting on adding some features, proper names and better way for handing some functions)
    Show Spoiler

    main code:
    Code:java
    1. public static java.awt.Color getBiome(long seed, int x, int z) {
    2. int size = 500;
    3.  
    4. int sectionX = x / size;
    5. if (x < 0) sectionX--;
    6.  
    7. int sectionZ = z / size;
    8. if (z < 0) sectionZ--;
    9.  
    10. ArrayList<Point> points = new ArrayList<>();
    11. if (map3.containsKey((sectionX * 12000) + (sectionZ))) {
    12. points = map3.get((sectionX * 12000) + (sectionZ));
    13. } else {
    14. Random random = new Random(seed + (sectionX * 12000/*6M/size (amount of sections on x axis)*/) + (sectionZ));
    15. for (int i = 0; i < 50; i++) {
    16. points.add(new Point(random.nextInt(size) + sectionX * size, random.nextInt(size) + sectionZ * size, getRandomColor(new Random())));
    17. }
    18. map3.put((sectionX * 12000) + (sectionZ), points);
    19. }
    20.  
    21. double closest = Double.MAX_VALUE;
    22. Point chosenPoint = new Point(Integer.MIN_VALUE, Integer.MAX_VALUE, null);
    23. for (Point d : points) {
    24. double distance = Math.sqrt(NumberConversions.square(d.getX() - x) + NumberConversions.square(d.getZ() - z)) * d.getWeight();
    25. if (distance < closest) {
    26. closest = distance;
    27. chosenPoint = d;
    28. }
    29. }
    30. Point tmpPoint = checkSurrounding(seed, x, z, closest);
    31. if (tmpPoint != null) {
    32. chosenPoint = tmpPoint;
    33. }
    34.  
    35. return chosenPoint.getColor();
    36. }


    get color (biome) (this is a perfect example of something that needs to change)
    Code:java
    1. private static Color getRandomColor(Random random) {
    2. List<java.awt.Color> colorList = Arrays.asList(java.awt.Color.GREEN, java.awt.Color.RED, java.awt.Color.BLUE, java.awt.Color.YELLOW, java.awt.Color.PINK, java.awt.Color.CYAN, java.awt.Color.MAGENTA);
    3. return colorList.get(randomColor.nextInt(colorList.size()));
    4. }
    5. public static Random randomColor = new Random(10);//what the hell is this?!?


    getting the region around and get the closest point (biome centerpoint)
    Code:java
    1. private static Point checkSurrounding(long seed, int x, int z, double closest) {
    2. int size = 500;
    3. Point chosenPoint = null;
    4. double tmpC = closest;
    5.  
    6. for (Dimension d : Arrays.asList(
    7. new Dimension(x + 1, z + 1),
    8. new Dimension(x + size, z),
    9. new Dimension(x + 1, z - 1),
    10. new Dimension(x, z + size),
    11. new Dimension(x, z - size),
    12. new Dimension(x - 1, z + 1),
    13. new Dimension(x - size, z),
    14. new Dimension(x - 1, z - 1))) {
    15.  
    16. int sectionX = d.width / size;
    17. if (d.width < 0) sectionX--;
    18.  
    19. int sectionZ = d.height / size;
    20. if (d.height < 0) sectionZ--;
    21.  
    22. ArrayList<Point> points = new ArrayList<>();
    23.  
    24. if (map3.containsKey((sectionX * 12000) + (sectionZ))) {
    25. points = (map3.get((sectionX * 12000) + (sectionZ)));
    26. } else {
    27. //create points
    28. Random random = new Random(seed + (sectionX * 12000/*6M/size (amount of sections on x axis)*/) + (sectionZ));
    29. for (int i = 0; i < 50; i++) {
    30. points.add(new Point(random.nextInt(size) + sectionX * size, random.nextInt(size) + sectionZ * size, getRandomColor(new Random())));
    31. }
    32. map3.put((sectionX * 12000) + (sectionZ), points);
    33. }
    34.  
    35. for (Point tmpD : points) {
    36. double distance = Math.sqrt(NumberConversions.square(tmpD.getX() - x) + NumberConversions.square(tmpD.getZ() - z)) * tmpD.getWeight();
    37. if (distance < tmpC) {
    38. tmpC = distance;
    39. chosenPoint = tmpD;
    40. }
    41. }
    42. }
    43.  
    44. return chosenPoint;
    45. }


    in here I save the regions (or sections), with this I don't need to re-create the section (saves loops, and time)
    private static HashMap<Integer, ArrayList<Point>> map3 = new HashMap<>();

    points class (need a rename)
    Code:java
    1.  
    2. public class Point {
    3.  
    4. private int x;
    5. private int z;
    6. private Color color;
    7.  
    8. public Point(int x, int z, Color color) {
    9. this.x = x;
    10. this.z = z;
    11. this.color = color;
    12. }
    13.  
    14. public int getZ() {
    15. return z;
    16. }
    17.  
    18. public int getX() {
    19. return x;
    20. }
    21.  
    22. public Color getColor() {
    23. return color;
    24. }
    25.  
    26. public double getWeight() {
    27. if (color.equals(Color.GREEN)) {
    28. return 1;
    29. } else if (color.equals(Color.RED)) {
    30. return 1.2;
    31. } else if (color.equals(Color.BLUE)) {
    32. return 1.4;
    33. } else if (color.equals(Color.YELLOW)) {
    34. return 1.2;
    35. } else if (color.equals(Color.PINK)) {
    36. return 1.1;
    37. } else if (color.equals(Color.CYAN)) {
    38. return 1.5;
    39. } else if (color.equals(Color.MAGENTA)) {
    40. return 1.3;
    41. }
    42. return 1;
    43. }
    44. }
    45.  
    46.  


     

Share This Page