11 juil. 2014, 12:15

Sommaire

Introduction

Dans ce nouveau tutoriel nous allons voir comment créer une nouvelle dimension. Dans une dimension il y a plusieurs composants, les biomes, le portail, la génération, les structures et plein d’autre éléments du décor. Dans ce tutoriel nous allons voir comment créer une dimension et aussi comment ajouter les éléments qui la composent.

Pré-requis

Ce tutoriel est relativement dur à comprendre, donc vous aurez, avant tout, besoin de bonnes connaissances en java.

Code

Ce tutoriel est très long, toutes les classes un peu longues seront mises dans un onglet spoiler.

La classe du Chunk Provider :

Cette classe est très importante dans votre dimension : beaucoup de choses y seront enregistrées, comme les lacs, les structures et les Noises.
Commencez en créant une nouvelle classe. Je vous conseille de la nommer comme ça : “ChunkProvider + le nom de la dimension, ou du mod”, ou bien “nom du mod ou de la dimension + ChunkProvider”, vous allez avoir beaucoup de classes il est donc important de pouvoir vite se retrouver dans ce gros paquet.
Une fois cette classe créée, ajoutez l’implémentation suivante :

implements IChunkProvider

Nous allons ajouter les méthodes prochainement.
Ensuite, ajoutez tous les fields :

    private Random rand;
    private NoiseGeneratorOctaves noiseGen1;//les noises
    private NoiseGeneratorOctaves noiseGen2;
    private NoiseGeneratorOctaves noiseGen3;
    private NoiseGeneratorPerlin noiseGen4;
    public NoiseGeneratorOctaves noiseGen5;
    public NoiseGeneratorOctaves noiseGen6;
    public NoiseGeneratorOctaves mobSpawnerNoise;
    private World worldObj;
    private WorldType worldType;
    private final double[] noiseArray;
    private final float[] parabolicField;
    private final boolean mapFeaturesEnabled;

    private MapGenBase caveGenerator = new MapGenCaves();//la génération des caves, remplacer par votre ravin si vous utiliser une autre stone
    private MapGenMineshaft mineshaftGenerator = new MapGenMineshaft();//la génération des mineshaft, ""        ""                     ""
    private MapGenScatteredFeature scatteredFeatureGenerator = new MapGenScatteredFeature();
    private MapGenBase ravineGenerator = new MapGenRavine();//la structure du ravin,          ""             ""                     ""

    private double[] stoneNoise = new double[256];
    private BiomeGenBase[] biomesForGeneration;
    double[] noiseData1; //les noises data
    double[] noiseData2;
    double[] noiseData3;
    double[] noiseData4;

Pour le moment ils ne sont pas initialisés, mais nous allons le faire (vous pouvez voir que nous générons des structures, donc remplacer les structures existantes par les vôtres, ou ajoutez-les), mais avant d’ajouter :

    {
        caveGenerator = TerrainGen.getModdedMapGen(caveGenerator, CAVE);
        mineshaftGenerator = (MapGenMineshaft) TerrainGen.getModdedMapGen(mineshaftGenerator, MINESHAFT);
        scatteredFeatureGenerator = (MapGenScatteredFeature) TerrainGen.getModdedMapGen(scatteredFeatureGenerator, SCATTERED_FEATURE);
        ravineGenerator = TerrainGen.getModdedMapGen(ravineGenerator, RAVINE);
    }

N’oubliez pas de tout importer ! Si jamais sur les : “CAVE, MINESHAFT …” il vous reste des erreurs, il faut tout importer, donc dans les imports ajoutez :

import static net.minecraftforge.event.terraingen.InitMapGenEvent.EventType.*;

Ceci nous permet (plus tard) de générer les structures.
Ensuite, nous allons initialiser les fields. Ajoutez cette fonction :

    public TutoChunkProvider(World world, long seed, boolean features)
    {
            this.worldObj = world;
            this.mapFeaturesEnabled = features;
            this.worldType = world.getWorldInfo().getTerrainType();
            this.rand = new Random(seed);
            this.noiseGen1 = new NoiseGeneratorOctaves(this.rand, 16);
            this.noiseGen2 = new NoiseGeneratorOctaves(this.rand, 16);
            this.noiseGen3 = new NoiseGeneratorOctaves(this.rand, 8);
            this.noiseGen4 = new NoiseGeneratorPerlin(this.rand, 4);
            this.noiseGen5 = new NoiseGeneratorOctaves(this.rand, 10);
            this.noiseGen6 = new NoiseGeneratorOctaves(this.rand, 16);
            this.mobSpawnerNoise = new NoiseGeneratorOctaves(this.rand, 8);
            this.noiseArray = new double[825];
            this.parabolicField = new float[25];

            for (int j = -2; j <= 2; ++j)
            {
                for (int k = -2; k <= 2; ++k)
                {
                    float f = 10.0F / MathHelper.sqrt_float((float)(j * j + k * k) + 0.2F);
                    this.parabolicField[j + 2 + (k + 2) * 5] = f;
                }
            }

            NoiseGenerator[] noiseGens = {noiseGen1, noiseGen2, noiseGen3, noiseGen4, noiseGen5, noiseGen6, mobSpawnerNoise};
            noiseGens = TerrainGen.getModdedNoiseGenerators(world, this.rand, noiseGens);
            this.noiseGen1 = (NoiseGeneratorOctaves)noiseGens[0];
            this.noiseGen2 = (NoiseGeneratorOctaves)noiseGens[1];
            this.noiseGen3 = (NoiseGeneratorOctaves)noiseGens[2];
            this.noiseGen4 = (NoiseGeneratorPerlin)noiseGens[3];
            this.noiseGen5 = (NoiseGeneratorOctaves)noiseGens[4];
            this.noiseGen6 = (NoiseGeneratorOctaves)noiseGens[5];
            this.mobSpawnerNoise = (NoiseGeneratorOctaves)noiseGens[6];
    }

Je ne vois pas quoi expliquer mis à part que nous initialisons les fields plus haut.
Normalement, il doit vous rester qu’une seule erreur vous demandant d’ajouter des fonctions, mais comme dit plus haut ne le faites pas.
Ensuite, nous allons ajouter une autre fonction, elle définira en quels blocs seront générés les lacs et le blocs dans le sol.

    public void func_147424_a(int Xchunks, int Zchunks, Block[] topBlock)
    {
        byte b0 = 63;
        this.biomesForGeneration = this.worldObj.getWorldChunkManager().getBiomesForGeneration(this.biomesForGeneration, Xchunks * 4 - 2, Zchunks * 4 - 2, 10, 10);
        this.func_147423_a(Xchunks * 4, 0, Zchunks * 4);

        final Random rand = new Random();

        for (int k = 0; k < 4; ++k)
        {
            int l = k * 5;
            int i1 = (k + 1) * 5;

            for (int j1 = 0; j1 < 4; ++j1)
            {
                int k1 = (l + j1) * 33;
                int l1 = (l + j1 + 1) * 33;
                int i2 = (i1 + j1) * 33;
                int j2 = (i1 + j1 + 1) * 33;

                for (int k2 = 0; k2 < 32; ++k2)
                {
                    double d0 = 0.105D;
                    double d1 = this.noiseArray[k1 + k2];
                    double d2 = this.noiseArray[l1 + k2];
                    double d3 = this.noiseArray[i2 + k2];
                    double d4 = this.noiseArray[j2 + k2];
                    double d5 = (this.noiseArray[k1 + k2 + 1] - d1) * d0;
                    double d6 = (this.noiseArray[l1 + k2 + 1] - d2) * d0;
                    double d7 = (this.noiseArray[i2 + k2 + 1] - d3) * d0;
                    double d8 = (this.noiseArray[j2 + k2 + 1] - d4) * d0;

                    for (int l2 = 0; l2 < 8; ++l2)
                    {
                        double d9 = 0.25D;
                        double d10 = d1;
                        double d11 = d2;
                        double d12 = (d3 - d1) * d9;
                        double d13 = (d4 - d2) * d9;

                        for (int i3 = 0; i3 < 4; ++i3)
                        {
                            int j3 = i3 + k * 4 << 12 | 0 + j1 * 4 << 8 | k2 * 8 + l2;
                            short short1 = 256;
                            j3 -= short1;
                            double d14 = 0.25D;
                            double d16 = (d11 - d10) * d14;
                            double d15 = d10 - d16;

                            for (int k3 = 0; k3 < 4; ++k3)
                            {
                                if ((d15 += d16) > 0.0D)
                                {
                                    topBlock[j3 += short1] = Blocks.stone;
                                }
                                else if (k2 * 8 + l2 < b0)
                                {
                                    topBlock[j3 += short1] = Blocks.water;
                                }
                                else
                                {
                                    topBlock[j3 += short1] = null;
                                }
                            }

                            d10 += d12;
                            d11 += d13;
                        }

                        d1 += d5;
                        d2 += d6;
                        d3 += d7;
                        d4 += d8;
                    }
                }
            }
        }
    }

Cette fonction nous permet de générer le sol, vous devriez avoir une erreur, car il vous manque une fonction qui sera ajoutée plus tard.
Pour la prochaine fonction nous donnons un biome, un biome qui devra être même partout dans toutes vos autres classes, vous pourrez bien entendu le changer par la suite.

Ajoutez :

    public void replaceBlocksForBiome(int coordX, int coordZ, Block[] block, byte[] arrayOfByte, BiomeGenBase[] biomeList)
    {
        ChunkProviderEvent.ReplaceBiomeBlocks event = new ChunkProviderEvent.ReplaceBiomeBlocks(this, coordX, coordZ, block, biomeList);
        MinecraftForge.EVENT_BUS.post(event);
        if (event.getResult() == Result.DENY) return;

        double d0 = 0.03125D;
        this.stoneNoise = this.noiseGen4.func_151599_a(this.stoneNoise, (double)(coordX * 16), (double)(coordZ * 16), 16, 16, d0 * 2.0D, d0 * 2.0D, 1.0D);

        for (int k = 0; k < 16; ++k)
        {
            for (int l = 0; l < 16; ++l)
            {
                BiomeGenPlains /*TODO ce biome est a remplacer par le votre*/ biomegenbase = (BiomeGenPlains /*TODO ce biome est a remplacer par le votre*/)biomeList[l + k * 16];
                biomegenbase.genTerrainBlocks(this.worldObj, this.rand, block, arrayOfByte, coordX * 16 + k, coordZ * 16 + l, this.stoneNoise[l + k * 16]);
            }
        }
    }

Cette fonction permet de changer les blocs par les blocs du biome.
Après cette fonction ajoutez cette autre fonction :

    public Chunk loadChunk(int par1, int par2)
    {
        return this.provideChunk(par1, par2);
    }

Cette fonction permet de charger ou générer des chunks spécifiques.

Ajoutez ensuite la fonction :

    public Chunk provideChunk(int par1, int par2)
    {
        this.rand.setSeed((long)par1 * 341873128712L + (long)par2 * 132897987541L);
        Block[] ablock = new Block[65536];
        byte[] abyte = new byte[65536];
        this.func_147424_a(par1, par2, ablock);
        this.biomesForGeneration = this.worldObj.getWorldChunkManager().loadBlockGeneratorData(this.biomesForGeneration, par1 * 16, par2 * 16, 16, 16);
        this.replaceBlocksForBiome(par1, par2, ablock, abyte, this.biomesForGeneration);
        this.caveGenerator.func_151539_a(this, this.worldObj, par1, par2, ablock);
        this.ravineGenerator.func_151539_a(this, this.worldObj, par1, par2, ablock);

        if (this.mapFeaturesEnabled)
        {
            this.mineshaftGenerator.func_151539_a(this, this.worldObj, par1, par2, ablock);
            this.scatteredFeatureGenerator.func_151539_a(this, this.worldObj, par1, par2, ablock);
        }

        Chunk chunk = new Chunk(this.worldObj, ablock, abyte, par1, par2);
        byte[] abyte1 = chunk.getBiomeArray();

        for (int k = 0; k < abyte1.length; ++k)
        {
            abyte1[k] = (byte)this.biomesForGeneration[k].biomeID;
        }

        chunk.generateSkylightMap();
        return chunk;
    }
  • Nous définissons qu’il récupère le seed (pas totalement sûr).
  • Génération des blocs par rapport au seed.
  • Ceci retourne à la fonction créer au début.
  • On initialise le field biomeForGeneration.
  • On dit qu’il peut générer certaines structures.
  • Dans la condition on met toutes les structures qui se génèrent quand la génération des structures est active.
  • Génère la lumière du ciel, gardez-le même si vous ne voulez pas de lumière, tout se fera dans une autre classe.

Après cette longue fonction ajoutez une fonction (encore plus longue :P) :

    private void func_147423_a(int x, int y, int z)
    {
        double d0 = 684.412D;
        double d1 = 684.412D;
        double d2 = 512.0D;
        double d3 = 512.0D;
        this.noiseData4 = this.noiseGen6.generateNoiseOctaves(this.noiseData4, x, z, 5, 5, 200.0D, 200.0D, 0.5D);
        this.noiseData1 = this.noiseGen3.generateNoiseOctaves(this.noiseData1, x, y, z, 5, 33, 5, 8.555150000000001D, 4.277575000000001D, 8.555150000000001D);
        this.noiseData2 = this.noiseGen1.generateNoiseOctaves(this.noiseData2, x, y, z, 5, 33, 5, 684.412D, 684.412D, 684.412D);
        this.noiseData3 = this.noiseGen2.generateNoiseOctaves(this.noiseData3, x, y, z, 5, 33, 5, 684.412D, 684.412D, 684.412D);
        boolean flag1 = false;
        boolean flag = false;
        int l = 0;
        int i1 = 0;
        double d4 = 8.5D;

        for (int j1 = 0; j1 < 5; ++j1)
        {
            for (int k1 = 0; k1 < 5; ++k1)
            {
                float f = 0.0F;
                float f1 = 0.0F;
                float f2 = 0.0F;
                byte b0 = 2;
                BiomeGenBase biomegenbase = this.biomesForGeneration[j1 + 2 + (k1 + 2) * 10];

                for (int l1 = -b0; l1 <= b0; ++l1)
                {
                    for (int i2 = -b0; i2 <= b0; ++i2)
                    {
                        BiomeGenBase biomegenbase1 = this.biomesForGeneration[j1 + l1 + 2 + (k1 + i2 + 2) * 10];
                        float f3 = biomegenbase1.rootHeight;
                        float f4 = biomegenbase1.heightVariation;

                        float f5 = this.parabolicField[l1 + 2 + (i2 + 2) * 5] / (f3 + 2.0F);

                        if (biomegenbase1.rootHeight > biomegenbase.rootHeight)
                        {
                            f5 /= 2.0F;
                        }

                        f += f4 * f5;
                        f1 += f3 * f5;
                        f2 += f5;
                    }
                }

                f /= f2;
                f1 /= f2;
                f = f * 0.9F + 0.1F;
                f1 = (f1 * 4.0F - 1.0F) / 8.0F;
                double d13 = this.noiseData4[i1] / 8000.0D;

                if (d13 < 0.0D)
                {
                    d13 = -d13 * 0.3D;
                }

                d13 = d13 * 3.0D - 2.0D;

                if (d13 < 0.0D)
                {
                    d13 /= 2.0D;

                    if (d13 < -1.0D)
                    {
                        d13 = -1.0D;
                    }

                    d13 /= 1.4D;
                    d13 /= 2.0D;
                }
                else
                {
                    if (d13 > 1.0D)
                    {
                        d13 = 1.0D;
                    }

                    d13 /= 8.0D;
                }

                ++i1;
                double d12 = (double)f1;
                double d14 = (double)f;
                d12 += d13 * 0.2D;
                d12 = d12 * 8.5D / 8.0D;
                double d5 = 8.5D + d13 * 4.0D;

                for (int j2 = 0; j2 < 33; ++j2)
                {
                    double d6 = ((double)j2 - d5) * 12.0D * 128.0D / 256.0D / d14;

                    if (d6 < 0.0D)
                    {
                        d6 *= 4.0D;
                    }

                    double d7 = this.noiseData2[l] / 512.0D;
                    double d8 = this.noiseData3[l] / 512.0D;
                    double d9 = (this.noiseData1[l] / 10.0D + 1.0D) / 2.0D;
                    double d10 = MathHelper.denormalizeClamp(d7, d8, d9) - d6;

                    if (j2 > 29)
                    {
                        double d11 = (double)((float)(j2 - 29) / 3.0F);
                        d10 = d10 * (1.0D - d11) + -10.0D * d11;
                    }

                    this.noiseArray[l] = d10;
                    ++l;
                }
            }
        }
    }

Je ne vais pas expliquer la fonction dans chaque détail, mais elle sera utilisée dans la génération du monde.
Ensuite, ajoutez la petite fonction :

    public boolean chunkExists(int par1, int par2)
    {
        return true;
    }

Cette fonction regarde si les chunks existent ou non.
Ensuite, nous allons ajouter une fonction vraiment pratique que je vous laisserais modifier à votre guise si vous en trouvez une utilité :

    public void populate(IChunkProvider chunkProvider, int x, int z)
    {
        BlockFalling.fallInstantly = true;
        int x1 = x * 16;
        int z1 = z * 16;
        BiomeGenBase biomegenbase = this.worldObj.getBiomeGenForCoords(x1 + 16, z1 + 16);
        this.rand.setSeed(this.worldObj.getSeed());
        long i1 = this.rand.nextLong() / 2L * 2L + 1L;
        long j1 = this.rand.nextLong() / 2L * 2L + 1L;
        this.rand.setSeed((long)x * i1 + (long)z * j1 ^ this.worldObj.getSeed());
        boolean flag = false;

        MinecraftForge.EVENT_BUS.post(new PopulateChunkEvent.Pre(chunkProvider, worldObj, rand, x, z, flag));

        // si l'option de génération des structures est activer alors nous générons le mineshaft et l'autre structure

        if (this.mapFeaturesEnabled)
        {
            this.mineshaftGenerator.generateStructuresInChunk(this.worldObj, this.rand, x, z);
            this.scatteredFeatureGenerator.generateStructuresInChunk(this.worldObj, this.rand, x, z);
        }

        int x2;
        int z2;
        int i2;

        //si le biome n'est pas un désert, une montagne du désert, et que la rand.nextIn(4) ne fait pas 0 alors nous avons un lac
        if (biomegenbase != BiomeGenBase.desert && biomegenbase != BiomeGenBase.desertHills && !flag && this.rand.nextInt(4) == 0
            && TerrainGen.populate(chunkProvider, worldObj, rand, x, z, flag, LAKE))
        {
            x2 = x1 + this.rand.nextInt(16) + 8;
            z2 = this.rand.nextInt(256);
            i2 = z1 + this.rand.nextInt(16) + 8;
                                //Le blocs de génération du lac
            (new WorldGenLakes(Blocks.water)).generate(this.worldObj, this.rand, x2, z2, i2);
        }

        //la condition est la même que en haut, sans les biomes 
        if (TerrainGen.populate(chunkProvider, worldObj, rand, x, z, flag, LAVA) && !flag && this.rand.nextInt(8) == 0)
        {
            x2 = x1 + this.rand.nextInt(16) + 8;
            //nous définissons les x, y, z
            z2 = this.rand.nextInt(this.rand.nextInt(248) + 8);
            i2 = z1 + this.rand.nextInt(16) + 8;

            if (z2 < 63 || this.rand.nextInt(10) == 0)
            {						//changer le par le blocs de votre choix
                (new WorldGenLakes(Blocks.lava)).generate(this.worldObj, this.rand, x2, z2, i2);
            }
        }

        //sa nous permet de générer des donjons, comme plus haut changer la classe par la votre pour générer vos donjons
        boolean doGen = TerrainGen.populate(chunkProvider, worldObj, rand, x, z, flag, DUNGEON);
        for (x2 = 0; doGen && x2 < 8; ++x2)
        {
            z2 = x1 + this.rand.nextInt(16) + 8;
            i2 = this.rand.nextInt(256);
            int j2 = z1 + this.rand.nextInt(16) + 8;
            //changer la classe par votre classe
            (new WorldGenDungeons()).generate(this.worldObj, this.rand, z2, i2, j2);
        }

        biomegenbase.decorate(this.worldObj, this.rand, x1, z1);
        //sa ajoute des mobs
        if (TerrainGen.populate(chunkProvider, worldObj, rand, x, z, flag, ANIMALS))
        {
        SpawnerAnimals.performWorldGenSpawning(this.worldObj, biomegenbase, x1 + 8, z1 + 8, 16, 16, this.rand);
        }
        x1 += 8;
        z1 += 8;

        //sa remplace la génération de l'eau par de la glasse, et ajoute la tomber de neige
        doGen = TerrainGen.populate(chunkProvider, worldObj, rand, x, z, flag, ICE);
        for (x2 = 0; doGen && x2 < 16; ++x2)
        {
            for (z2 = 0; z2 < 16; ++z2)
            {
                i2 = this.worldObj.getPrecipitationHeight(x1 + x2, z1 + z2);

                if (this.worldObj.isBlockFreezable(x2 + x1, i2 - 1, z2 + z1))
                {
                    this.worldObj.setBlock(x2 + x1, i2 - 1, z2 + z1, Blocks.ice, 0, 2);
                }

                if (this.worldObj.func_147478_e(x2 + x1, i2, z2 + z1, true))
                {
                    this.worldObj.setBlock(x2 + x1, i2, z2 + z1, Blocks.snow_layer, 0, 2);
                }
            }
        }

        MinecraftForge.EVENT_BUS.post(new PopulateChunkEvent.Post(chunkProvider, worldObj, rand, x, z, flag));

        BlockFalling.fallInstantly = false;
    }

Si vous avez des erreurs sur les attributs LAVA, ANIMALS, DUNGEONS, ICE… Importez manuellement :

import static net.minecraftforge.event.terraingen.PopulateChunkEvent.Populate.EventType.*;

Même si le code est un peu expliqué je vais mieux vous l’expliquer.

  • Les premiers int (long …) ne vous en préoccupez pas ce sont des choses comme les positions y …
  • Ensuite, nous avons la condition que si nous activons les structures, nous générons les structures (qui sont dans cette condition), vous pouvez changer par vos classes.
  • Nous déclarons des ints, qui seront initialisés par la suite, il correspondent aux trois vecteurs x, y et z.
  • Nous avons une condition qui nous permet de vérifier que ça ne soit pas un désert et qu’un int soit égal à une valeur. Si la condition est vérifiée nous générons un lac (vous pouvez changer par votre classe de génération), et nous mettons en quels blocs pour mon cas ça sera en bloc d’eau, mais vous pouvez changer par votre propre bloc.
  • La condition est presque identique à la précédente sauf que nous générons un lac de lave.
  • Nous ajoutons la génération des donjons.
  • Ensuite, nous ajoutons l’apparition de certains mobs.
  • Cela nous permet de changer des blocs en glace (bloc d’eau) et de faire tomber de la neige.
  • N’oubliez pas que vous pouvez changer par vos classes et qu’ils ne sont pas tous essentiel.

Nous allons ajouter le reste des fonctions de la classe, ajoutez :

    public boolean saveChunks(boolean par1, IProgressUpdate par2IProgressUpdate)
    {
        return true;
    }

    public void saveExtraData() {}

    public boolean unloadQueuedChunks()
    {
        return false;
    }

    public boolean canSave()
    {
        return true;
    }

    public String makeString()
    {
        return "RandomLevelSource";
    }

    public List getPossibleCreatures(EnumCreatureType par1EnumCreatureType, int par2, int par3, int par4)
    {
        /*TODO*/BiomeGenPlains biomegenbase = (/*TODO*/BiomeGenPlains)this.worldObj.getBiomeGenForCoords(par2, par4);
        return par1EnumCreatureType == EnumCreatureType.monster && this.scatteredFeatureGenerator.func_143030_a(par2, par3, par4) ? this.scatteredFeatureGenerator.getScatteredFeatureSpawnList() : biomegenbase.getSpawnableList(par1EnumCreatureType);
    }

    public ChunkPosition func_147416_a(World world, String strg, int x, int y, int z)
    {
        return null;
    }

    public int getLoadedChunkCount()
    {
        return 0;
    }

    public void recreateStructures(int par1, int par2)
    {
        if (this.mapFeaturesEnabled)
        {
            this.mineshaftGenerator.func_151539_a(this, this.worldObj, par1, par2, (Block[])null);
            this.scatteredFeatureGenerator.func_151539_a(this, this.worldObj, par1, par2, (Block[])null);
        }
    }

N’oubliez pas de tout importer (Ctrl + shift + o) et normalement toutes les erreurs devraient partir.
Mais à quoi ça correspond ? (la liste est dans l’ordre des fonctions) :

  • Ceci permet de sauvegarder tous les niveaux en une seule fois.
  • Cela permet de sauvegarder ce qui ne concerne pas les chunks.
  • Cette fonction permet de décharger certains chunks.
  • Je pense qu’un minimum d’anglais s’impose si vous ne comprenez pas, ça veut dire : les chunks supportent-ils la sauvegarde ? Dans notre cas nous mettons oui.
  • Cela convertit des données en chose lisible pour le monde (oui, moi aussi je n’aime pas dire chose, truc …).
  • Voilà la liste des créatures qui peuvent spawn, mais dans la classe cela n’est pas très important, car ça se passe dans une autre classe.
  • Je crois bien que ceci est pour une génération. Nous nous en avons pas elle est donc null.
  • Là nous démontrons le nombre de chunks qui doivent rester chargés.
  • Nous ajoutons la liste habituelle des structures (quand l’option “Générer des structures” est sur “ON”).
    Voilà nous en avons enfin fini avec cette très longue classe d’environ 520 lignes, reste plus qu’à faire la suite.

De plus, si jamais vous voulez des ravins, des grottes… Il vous faut créer vos propres classes si vous utilisez une autre stone et remplacer dans le code (en créant une classe similaire) Blocks.stone par votre bloc.

La classe GenLayer :

La classe GenLayer nous permet la génération de nos biomes, nous pouvons y configurer les endroits où seront les biomes dans le monde, dans cette classe nous allons seulement faire la génération de biome.
Donc comme toujours, créez une classe abstraite et ajoutez l’extension :

GenLayer

Ensuite, importez la classe (Ctrl + shift + o).
Ce qui doit vous donner :

public abstract class TutoGenLayer extends  GenLayer
{
    public TutoGenLayer(long par1) 
    {
        super(par1);
    }
}

Ensuite, ajoutez la fonction suivante :

    public static GenLayer[] makeTheWorld(long l)
    {

    }

Dans cette fonction nous ajouterons les lignes de code permettant de générer des biomes et le monde en général.
Maintenant complétez en ajoutant :

        GenLayer biomes = new TutoGenLayerBiome(1L);

        biomes = new GenLayerZoom(1000L, biomes);
        biomes = new GenLayerZoom(1001L, biomes);
        biomes = new GenLayerZoom(1002L, biomes);
        biomes = new GenLayerZoom(1003L, biomes);
        biomes = new GenLayerZoom(1004L, biomes);
        biomes = new GenLayerZoom(1005L, biomes);

        GenLayer genlayervoronoizoom = new GenLayerVoronoiZoom(10L, biomes);
        biomes.initWorldGenSeed(l);
        genlayervoronoizoom.initWorldGenSeed(l);

        return new GenLayer[] {biomes, genlayervoronoizoom};
  • Ici, nous donnons les biomes que nous aurons. Cette classe n’est pas encore créée donc vous devriez avoir une erreur.
  • Les GenLayerZoom sont les valeurs de la génération de couche; elle peut concerner d’autre informations que les couches, comme la température etc…
  • Ceci donne encore des informations sur le monde.
  • Nous prenons en compte le seed du monde.
  • Idem :).
  • Nous retournons par un tableau au “biomes”, et au “genlayervoronoizoo”.
    Nous en avons fini avec cette classe, nous passons donc à une autre classe.

La classe BiomeGenLayer :

Dans cette classe nous enregistrerons tous les biomes de notre dimension. Commencez en créant une classe et en lui ajoutant GenLayer comme classe mère.

Ensuite ajoutez :

    protected BiomeGenBase[] baseBiome = {BiomeGenBase.//Biome};

    public TutoBiomeGenLayer(long seed)
    {
        super(seed);
    }

    @Override
    public int[] getInts(int coordX, int coordZ, int width, int depth) 
    {

    }

Ici, nous ajoutons un tableau. Vous devez mettre toujours le biome que vous avez mis dans vos autres classes, ou que vous mettrez plus tard, mais dans ce tableau tous les biomes devront être mis. Tous les biomes doivent avoir l’extends de votre biome de base (le biome mis dans toutes les autres classes), ou de une de vos classes, mais nous reparlerons de ça plus tard.
Complétez la fonction getInts en ajoutant :

        int[] dest = IntCache.getIntCache(width * depth);

        for(int dz = 0; dz < depth; dz++)
        {
            for(int dx = 0; dx < width; dx++)
            {
                this.initChunkSeed(dx + coordX, dz + coordZ);
                dest[(dx + dz * width)] = this.baseBiome[nextInt(this.baseBiome.length)].biomeID;
            }
        }
        return dest;

Cela permettra la génération de vos biomes. Nous en avons fini avec les classes GenLayer.

La classe Teleporter :

La classe “Teleporter” sera la classe qui permettra d’envoyer le joueur dans notre dimension.

Pour commencer ajoutez cette extension :

    Teleporter

puis ceci :

    private final WorldServer worldServerInstance;
    private final Random random;
    private final LongHashMap destinationCoordinateCache = new LongHashMap();

    private final List destinationCoordinateKeys = new ArrayList();

    public TutoTeleporter(WorldServer worldServer)
    {

    }

    public void placeInPortal(Entity entity, double x, double y, double z, float rotationYaw)
    {

    }

    public boolean placeInExistingPortal(Entity entity, double x, double y, double z, float rotationYaw)
    {

    }

    public boolean makePortal(Entity entity)
    {

    }

Les fonctions permettent de :

  • La fonction (constructeur) qui correspond au nom de votre classe permet d’initialiser des fields.
  • “placeInPortal”, permet de placer l’entité dans un portail ou de le créer (appelle les deux autres fonctions).
  • “placeInExistingPortal” va mettre l’entité dans un portail déjà existant s’il existe.
  • “makePortal” va permettre de créer notre portail au passage de la dimension, la fonction est appelé uniquement si aucun portail est trouvé.

Complétez la première fonction (constructeur) avec :

        super(worldServer);
        this.worldServerInstance = worldServer;
        this.random = new Random(worldServer.getSeed());

Ici, nous initialisons des fields.
Dans la fonction placeInPortal ajoutez la condition :

        if(this.worldServerInstance.provider.dimensionId != 1)
        {
            if(!this.placeInExistingPortal(entity, x, y, z, rotationYaw))
            {
                this.makePortal(entity);
                this.placeInExistingPortal(entity, x, y, z, rotationYaw);
            }
        }

Cette fonction vas regarder si la dimension n’est pas la dimension portant l’ID 1 (l’overworld).
Ensuite, ajoutez :

        else
        {
            int i = MathHelper.floor_double(entity.posX);//position x
            int j = MathHelper.floor_double(entity.posY) - 1;//position y
            int k = MathHelper.floor_double(entity.posZ);//position z
            byte b0 = 1;
            byte b1 = 0;
            for(int l = -2; l <= 2; ++l)
            {
                for(int i1 = -2; i1 <= 2; ++i1)
                {
                    for(int j1 = -1; j1 < 3; ++j1)
                    {
                        int k1 = i + i1 * b0 + l * b1;
                        int l1 = j + j1;
                        int i2 = k + i1 * b1 - l * b0;
                        boolean flag = j1 < 0;

                        //set la structure
                        this.worldServerInstance.setBlock(k1, l1, i2, flag ? Blocks.stone : Blocks.air);
                    }
                }
            }
            entity.setLocationAndAngles((double)i, (double)j, (double)k, entity.rotationYaw, 0.0F);
            entity.motionX = entity.motionY = entity.motionZ = 0.0D;
        }

permet de générer comme il faut la structure.
Ensuite, dans la troisième fonction :

        short short1 = 128;
        double d3 = -1.0D;
        int i = 0;
        int j = 0;
        int k = 0;
        int l = MathHelper.floor_double(entity.posX);// position x
        int i1 = MathHelper.floor_double(entity.posZ);// position z
        long j1 = ChunkCoordIntPair.chunkXZ2Int(l, i1);// convertit en un nombre
                                                        // entier pour le hach
        boolean flag = true;
        double d4;
        int k1;

        if(this.destinationCoordinateCache.containsItem(j1))
        {
            PortalPosition portalposition = (PortalPosition)this.destinationCoordinateCache.getValueByKey(j1);
            d3 = 0.0D;
            i = portalposition.posX;// position x
            j = portalposition.posY;// position y
            k = portalposition.posZ;// position z
            portalposition.lastUpdateTime = this.worldServerInstance.getTotalWorldTime();
            flag = false;
        }

        else
        {
            for(k1 = l - short1; k1 <= l + short1; ++k1)
            {
                double d5 = (double)k1 + 0.5D - entity.posX;
                for(int l1 = i1 - short1; l1 <= i1 + short1; ++l1)
                {
                    double d6 = (double)l1 + 0.5D - entity.posZ;
                    // renvoie a la hauteur du monde
                    for(int i2 = this.worldServerInstance.getActualHeight() - 1; i2 >= 0; --i2)
                    {
                        if(this.worldServerInstance.getBlock(k1, i2, l1) == TutoDimension.tutoPortail)
                        {
                            while(this.worldServerInstance.getBlock(k1, i2 - 1, l1) == TutoDimension.tutoPortail)
                            {
                                --i2;
                            }
                            d4 = (double)i2 + 0.5D - entity.posY;
                            double d7 = d5 * d5 + d4 * d4 + d6 * d6;
                            if(d3 < 0.0D || d7 < d3)
                            {
                                d3 = d7;
                                i = k1;
                                j = i2;
                                k = l1;
                            }
                        }
                    }
                }
            }
        }

        if(d3 >= 0.0D)
        {
            if(flag)
            { // ajoute des valeurs au LongHashMap
                this.destinationCoordinateCache.add(j1, new TutoPortalPosition(this, i, j, k, this.worldServerInstance.getTotalWorldTime()));
                this.destinationCoordinateKeys.add(Long.valueOf(j1));
            }
            double d8 = (double)i + 0.5D;
            double d9 = (double)j + 0.5D;
            d4 = (double)k + 0.5D;
            int j2 = -1;
            if(this.worldServerInstance.getBlock(i - 1, j, k) == TutoDimension.tutoPortail)
            {
                j2 = 2;
            }
            if(this.worldServerInstance.getBlock(i + 1, j, k) == TutoDimension.tutoPortail)
            {
                j2 = 0;
            }
            if(this.worldServerInstance.getBlock(i, j, k - 1) == TutoDimension.tutoPortail)
            {
                j2 = 3;
            }
            if(this.worldServerInstance.getBlock(i, j, k + 1) == TutoDimension.tutoPortail)
            {
                j2 = 1;
            }
            int k2 = entity.getTeleportDirection();
            if(j2 > -1)
            { // la position de l'entité dans le monde après avoir passer le
                // portail
                int l2 = Direction.rotateLeft[j2];
                int i3 = Direction.offsetX[j2];
                int j3 = Direction.offsetZ[j2];
                int k3 = Direction.offsetX[l2];
                int l3 = Direction.offsetZ[l2];
                boolean flag1 = !this.worldServerInstance.isAirBlock(i + i3 + k3, j, k + j3 + l3) || !this.worldServerInstance.isAirBlock(i + i3 + k3, j + 1, k + j3 + l3);
                boolean flag2 = !this.worldServerInstance.isAirBlock(i + i3, j, k + j3) || !this.worldServerInstance.isAirBlock(i + i3, j + 1, k + j3);
                if(flag1 && flag2)
                {
                    j2 = Direction.rotateOpposite[j2];
                    l2 = Direction.rotateOpposite[l2];
                    i3 = Direction.offsetX[j2];
                    j3 = Direction.offsetZ[j2];
                    k3 = Direction.offsetX[l2];
                    l3 = Direction.offsetZ[l2];
                    k1 = i - k3;
                    d8 -= (double)k3;
                    int i4 = k - l3;
                    d4 -= (double)l3;
                    flag1 = !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j, i4 + j3 + l3) || !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j + 1, i4 + j3 + l3);
                    flag2 = !this.worldServerInstance.isAirBlock(k1 + i3, j, i4 + j3) || !this.worldServerInstance.isAirBlock(k1 + i3, j + 1, i4 + j3);
                }
                float f1 = 0.5F;
                float f2 = 0.5F;
                if(!flag1 && flag2)
                {
                    f1 = 1.0F;
                }
                else if(flag1 && !flag2)
                {
                    f1 = 0.0F;
                }
                else if(flag1 && flag2)
                {
                    f2 = 0.0F;
                }
                d8 += (double)((float)k3 * f1 + f2 * (float)i3);
                d4 += (double)((float)l3 * f1 + f2 * (float)j3);
                float f3 = 0.0F;
                float f4 = 0.0F;
                float f5 = 0.0F;
                float f6 = 0.0F;
                if(j2 == k2)
                {
                    f3 = 1.0F;
                    f4 = 1.0F;
                }
                else if(j2 == Direction.rotateOpposite[k2])
                {
                    f3 = -1.0F;
                    f4 = -1.0F;
                }
                else if(j2 == Direction.rotateRight[k2])
                {
                    f5 = 1.0F;
                    f6 = -1.0F;
                }
                else
                {
                    f5 = -1.0F;
                    f6 = 1.0F;
                }
                double d10 = entity.motionX;
                double d11 = entity.motionZ;
                entity.motionX = d10 * (double)f3 + d11 * (double)f6;
                entity.motionZ = d10 * (double)f5 + d11 * (double)f4;
                entity.rotationYaw = rotationYaw - (float)(k2 * 90) + (float)(j2 * 90);
            }
            else
            {
                entity.motionX = entity.motionY = entity.motionZ = 0.0D;
            }
            entity.setLocationAndAngles(d8, d9, d4, entity.rotationYaw, entity.rotationPitch);
            return true;
        }
        else
        {
            return false;
        }

Il est très dur de vous décrire le code, je pense qu’en regardant vous comprendrez.
Ensuite, dans la dernière fonction ajoutez :

        byte b0 = 16;
        double d0 = -1.0D;
        int x = MathHelper.floor_double(entity.posX);// position x
        int y = MathHelper.floor_double(entity.posY);// position y
        int z = MathHelper.floor_double(entity.posZ);// position z
        int l = x;
        int i1 = y;
        int j1 = z;
        int k1 = 0;
        int l1 = this.random.nextInt(4);
        int i2;
        double d1;
        int k2;
        double d2;
        int i3;
        int j3;
        int k3;
        int l3;
        int i4;
        int j4;
        int k4;
        int l4;
        int i5;
        double d3;
        double d4;

        for(i2 = x - b0; i2 <= x + b0; ++i2)
        {
            d1 = (double)i2 + 0.5D - entity.posX;

            for(k2 = z - b0; k2 <= z + b0; ++k2)
            {
                d2 = (double)k2 + 0.5D - entity.posZ;
                label274:

                for(i3 = this.worldServerInstance.getActualHeight() - 1; i3 >= 0; --i3)
                {
                    // si le blocs et un bloc d'air
                    if(this.worldServerInstance.isAirBlock(i2, i3, k2))
                    {
                        while(i3 > 0 && this.worldServerInstance.isAirBlock(i2, i3 - 1, k2))
                        {
                            --i3;
                        }

                        for(j3 = l1; j3 < l1 + 4; ++j3)
                        {
                            k3 = j3 % 2;
                            l3 = 1 - k3;

                            if(j3 % 4 >= 2)
                            {
                                k3 = -k3;
                                l3 = -l3;
                            }

                            for(i4 = 0; i4 < 3; ++i4)
                            {
                                for(j4 = 0; j4 < 4; ++j4)
                                {
                                    for(k4 = -1; k4 < 4; ++k4)
                                    {
                                        l4 = i2 + (j4 - 1) * k3 + i4 * l3;
                                        i5 = i3 + k4;
                                        int j5 = k2 + (j4 - 1) * l3 - i4 * k3;

                                        // si k4 et inférieur a 0 et que le
                                        // blocs n'est pas solide ou si k4 et
                                        // égale ou inférieur a 0 et que le bloc
                                        // est pas d'air nous retournons ou
                                        // label274 plus haut
                                        if(k4 < 0 && !this.worldServerInstance.getBlock(l4, i5, j5).getMaterial().isSolid() || k4 >= 0 && !this.worldServerInstance.isAirBlock(l4, i5, j5))
                                        {
                                            continue label274;
                                        }
                                    }
                                }
                            }

                            d3 = (double)i3 + 0.5D - entity.posY;
                            d4 = d1 * d1 + d3 * d3 + d2 * d2;

                            if(d0 < 0.0D || d4 < d0)
                            {
                                d0 = d4;
                                l = i2;
                                i1 = i3;
                                j1 = k2;
                                k1 = j3 % 4;
                            }
                        }
                    }
                }
            }
        }

        if(d0 < 0.0D)
        {
            for(i2 = x - b0; i2 <= x + b0; ++i2)
            {
                d1 = (double)i2 + 0.5D - entity.posX;

                for(k2 = z - b0; k2 <= z + b0; ++k2)
                {
                    d2 = (double)k2 + 0.5D - entity.posZ;
                    label222:

                    for(i3 = this.worldServerInstance.getActualHeight() - 1; i3 >= 0; --i3)
                    {
                        if(this.worldServerInstance.isAirBlock(i2, i3, k2))
                        {
                            while(i3 > 0 && this.worldServerInstance.isAirBlock(i2, i3 - 1, k2))
                            {
                                --i3;
                            }

                            for(j3 = l1; j3 < l1 + 2; ++j3)
                            {
                                k3 = j3 % 2;
                                l3 = 1 - k3;

                                for(i4 = 0; i4 < 4; ++i4)
                                {
                                    for(j4 = -1; j4 < 4; ++j4)
                                    {
                                        k4 = i2 + (i4 - 1) * k3;
                                        l4 = i3 + j4;
                                        i5 = k2 + (i4 - 1) * l3;
                                        // si k4 et inférieur a 0 et que le
                                        // blocs n'est pas solide ou si k4 et
                                        // égale ou inférieur a 0 et que le bloc
                                        // est pas d'air nous retournons ou
                                        // label222 plus haut
                                        if(j4 < 0 && !this.worldServerInstance.getBlock(k4, l4, i5).getMaterial().isSolid() || j4 >= 0 && !this.worldServerInstance.isAirBlock(k4, l4, i5))
                                        {
                                            continue label222;
                                        }
                                    }
                                }

                                d3 = (double)i3 + 0.5D - entity.posY;
                                d4 = d1 * d1 + d3 * d3 + d2 * d2;

                                if(d0 < 0.0D || d4 < d0)
                                {
                                    d0 = d4;
                                    l = i2;
                                    i1 = i3;
                                    j1 = k2;
                                    k1 = j3 % 2;
                                }
                            }
                        }
                    }
                }
            }
        }

        int k5 = l;
        int j2 = i1;
        k2 = j1;
        int l5 = k1 % 2;
        int l2 = 1 - l5;

        if(k1 % 4 >= 2)
        {
            l5 = -l5;
            l2 = -l2;
        }

        boolean flag;

        if(d0 < 0.0D)
        {
            if(i1 < 70)
            {
                i1 = 70;
            }

            if(i1 > this.worldServerInstance.getActualHeight() - 10)
            {
                i1 = this.worldServerInstance.getActualHeight() - 10;
            }

            j2 = i1;

            for(i3 = -1; i3 <= 1; ++i3)
            {
                for(j3 = 1; j3 < 3; ++j3)
                {
                    for(k3 = -1; k3 < 3; ++k3)
                    {
                        l3 = k5 + (j3 - 1) * l5 + i3 * l2;
                        i4 = j2 + k3;
                        j4 = k2 + (j3 - 1) * l2 - i3 * l5;
                        flag = k3 < 0;
                        // permet de set des blocs de stone et air au bonne
                        // endroit
                        this.worldServerInstance.setBlock(l3, i4, j4, flag ? Blocks.stone : Blocks.air);
                    }
                }
            }
        }

        for(i3 = 0; i3 < 4; ++i3)
        {
            for(j3 = 0; j3 < 4; ++j3)
            {
                for(k3 = -1; k3 < 4; ++k3)
                {
                    l3 = k5 + (j3 - 1) * l5;
                    i4 = j2 + k3;
                    j4 = k2 + (j3 - 1) * l2;
                    flag = j3 == 0 || j3 == 3 || k3 == -1 || k3 == 3;
                    // permet de set des blocs de stone et portail au bonne
                    // endroit
                    this.worldServerInstance.setBlock(l3, i4, j4, (flag ? Blocks.stone : TutoDimension.tutoPortail), 0, 2);
                }
            }

            for(j3 = 0; j3 < 4; ++j3)
            {
                for(k3 = -1; k3 < 4; ++k3)
                {
                    l3 = k5 + (j3 - 1) * l5;
                    i4 = j2 + k3;
                    j4 = k2 + (j3 - 1) * l2;
                    this.worldServerInstance.notifyBlocksOfNeighborChange(l3, i4, j4, this.worldServerInstance.getBlock(l3, i4, j4));
                }
            }
        }

        return true;
    }

Cette fonction permet de faire le portail en modifiant les int des l3, i4, j4, nous pouvons changer le portail.
Après cette fonction nous allons ajouter la classe interne :

class TutoPortalPosition extends ChunkCoordinates
{

}

Terminez la fonction avec :

    public long time;
    final TutoTeleporter teleporter;

    public TutoPortalPosition(TutoTeleporter teleporter, int x, int y, int z, long creationTime)
    {

    }

    @Override
    public int compareTo(Object o)
    {

    }

Dans la fonction ajoutez :

        super(x, y, z);
        this.teleporter = teleporter;
        this.time = creationTime;

Ceci est le temps où le portail sera vérifié.
Nous en avons fini avec la classe pleine de calcul qui est le “Teleporter”.

WorldChunkManager

Commencez à ajouter l’extension :

extends WorldChunkManager

Dans cette classe ajoutez tout ça, nous compléterons fonction par fonction. Ajoutez :

    /**
        * GenLayer = classe de GenLayer si vous utiliser un GenLayer qui n’hérite
        * pas de la classe GenLayer alors il faut changer par votre classe
        */
    private GenLayer genBiomes;
    private GenLayer biomeIndexLayer;
    /** L'objet du BiomeCache pour le monde */
    private BiomeCache biomeCache;
    /** List des biome ou le joueur peu spawn */
    private List<BiomeGenBase> biomesToSpawnIn;

    protected TutoWorldChunkManager()
    {

    }

    public TutoWorldChunkManager(long par1, WorldType worldType)
    {

    }

    public TutoWorldChunkManager(World world)
    {

    }

    /** Permet d'obtenir la list des biomes ou le joueur peut apparaitre */
    public List<BiomeGenBase> getBiomesToSpawnIn()
    {

    }

    /** Renvoie au BiomeGenBase a la position x, z relative dans le monde */
    public BiomeGenBase getBiomeGenAt(int x, int z)
    {

    }

    /**
        * Retourne a la list des valeurs pour la pluis... ... pour en fonction de
        * blocs spésifier
        */
    public float[] getRainfall(float[] par1ArrayOfFloat, int par2, int par3, int par4, int par5)
    {

    }

    /** Retourne a une température donner par raport au y */
    @SideOnly(Side.CLIENT)
    public float getTemperatureAtHeight(float par1, int par2)
    {

    }

    /** Retourne à un tableau pour les biomes a générer */
    public BiomeGenBase[] getBiomesForGeneration(BiomeGenBase[] par1ArrayOfBiomeGenBase, int par2, int par3, int par4, int par5)
    {

    }

    /** Retourne au biome et charge les données pour les blocs */
    public BiomeGenBase[] loadBlockGeneratorData(BiomeGenBase[] par1ArrayOfBiomeGenBase, int par2, int par3, int par4, int par5)
    {

    }

    /**
        * Retourne a une liste de biome pour les blocs spécifiés argument : x, y,
        * largeur, longueur, cache.
        */
    public BiomeGenBase[] getBiomeGenAt(BiomeGenBase[] arrayOfBiomeGenBase, int x, int y, int width, int length, boolean cacheFlag)
    {

    }

    /** Verifie les données du chunk */
    public boolean areBiomesViable(int x, int y, int z, List par4List)
    {

    }

    /** Trouve un biome dans un chunk */
    public ChunkPosition findBiomePosition(int x, int y, int z, List list, Random rand)
    {

    }

    /** Supprime le biome dans les 30s. si il n'est pas vérifier dans la temps */
    public void cleanupCache()
    {

    }
}

Nous ajoutons quelques fonctions et des fields. Tous est décrit donc vous avez seulement à lire.
Dans la première fonction (constructeur) ajoutez :

        this.biomeCache = new BiomeCache(this);
        // liste des biomes ou nous spawons
        this.biomesToSpawnIn = new ArrayList<BiomeGenBase>();
        // Ce biome n'est pas le biome ou vous spawner forcement, mais dans
        // toute les classes il devra être mis
        this.biomesToSpawnIn.add(BiomeGenBase.forest);

Après dans la seconde fonction ajoutez :

        this();
        /**
            * Appele la fonction makeTheWorld dans la classe TutoGenLayer, la
            * fonction est la fonction de génération de nos biome
            */
        GenLayer[] agenlayer = TutoGenLayer.makeTheWorld(par1);
        this.genBiomes = agenlayer[0];
        this.biomeIndexLayer = agenlayer[1];

Ajoutez dans la troisième fonction :

        this(world.getSeed(), world.provider.terrainType);

Dans la fonction suivante il vous faut ajouter :

        return this.biomesToSpawnIn;

Ce qui permet de retourner à la liste des biomes où le joueur peut spawn.
Dans la fonction getBiomeGenAt ajoutez :

        BiomeGenBase biome = this.biomeCache.getBiomeGenAt(x, z);
        if(biome == null)
        {
            return BiomeGenBase.forest;

        }
        return biome;

La fonction vous est expliquée.
Ensuite, ajoutez la fonction suivante :

        if(par1ArrayOfFloat == null || par1ArrayOfFloat.length < par4 * par5)
        {
            par1ArrayOfFloat = new float[par4 * par5];
        }

        Arrays.fill(par1ArrayOfFloat, 0, par4 * par5, 0.0F);
        return par1ArrayOfFloat;

La fonction est décrite juste au-dessus.
Dans le getTemperatureAtHeight ajoutez :

                return par 1

Après ajoutez dans la fonction suivante :

        IntCache.resetIntCache();

        if(par1ArrayOfBiomeGenBase == null || par1ArrayOfBiomeGenBase.length < par4 * par5)
        {
            par1ArrayOfBiomeGenBase = new BiomeGenBase[par4 * par5];
        }

        int[] aint = this.genBiomes.getInts(par2, par3, par4, par5);

        for(int i = 0; i < par4 * par5; ++i)
        {
            if(aint* >= 0)
            {
                par1ArrayOfBiomeGenBase* = BiomeGenBase.getBiome(aint*);
            }
            else
            {
                par1ArrayOfBiomeGenBase* = BiomeGenBase.forest;
            }
        }

        return par1ArrayOfBiomeGenBase;

La fonction est aussi mise au dessus.
La fonction loadBlockGeneratorData est à compléter avec :

        return this.getBiomeGenAt(par1ArrayOfBiomeGenBase, par2, par3, par4, par5, true);

Dans la fonction getBiomeGenAt terminez avec :

        IntCache.resetIntCache();

        if(arrayOfBiomeGenBase == null || arrayOfBiomeGenBase.length < width * length)
        {
            arrayOfBiomeGenBase = new BiomeGenBase[width * length];
        }

        if(cacheFlag && width == 16 && length == 16 && (x & 15) == 0 && (y & 15) == 0)
        {
            BiomeGenBase[] abiomegenbase1 = this.biomeCache.getCachedBiomes(x, y);
            System.arraycopy(abiomegenbase1, 0, arrayOfBiomeGenBase, 0, width * length);
            return arrayOfBiomeGenBase;
        }
        else
        {
            int[] aint = this.biomeIndexLayer.getInts(x, y, width, length);

            for(int i1 = 0; i1 < width * length; ++i1)
            {
                if(aint[i1] >= 0)
                {
                    arrayOfBiomeGenBase[i1] = BiomeGenBase.getBiome(aint[i1]);
                }

                else
                {
                    arrayOfBiomeGenBase[i1] = BiomeGenBase.forest;
                }
            }

            return arrayOfBiomeGenBase;
        }

La fonction est aussi expliquée.
Après ceci, ajoutez dans la fonction :

        IntCache.resetIntCache();
        int l = x - z >> 2;
        int i1 = y - z >> 2;
        int j1 = x + z >> 2;
        int k1 = y + z >> 2;
        int l1 = j1 - l + 1;
        int i2 = k1 - i1 + 1;
        int[] aint = this.genBiomes.getInts(l, i1, l1, i2);

        for(int j2 = 0; j2 < l1 * i2; ++j2)
        {
            BiomeGenBase biomegenbase = BiomeGenBase.getBiome(aint[j2]);

            if(!par4List.contains(biomegenbase))
            {
                return false;
            }
        }

        return true;

La fonction est aussi décrite au dessus.
Dans la dernière fonction qui est aussi décrite ajoutez :

        this.biomeCache.cleanupCache();

Nous en avons fini avec cette classe.

WorldProvider :

Pour cette classe ajoutez l’extension suivante :

extends WorldProvider

Ensuite, ajoutez une fonction :

    public void registerWorldChunkManager()
    {

    }

Nous allons un peu la compléter, donc ajoutez :

        this.worldChunkMgr = new TutoWorldChunkManager(worldObj.getSeed(), terrainType);
        this.dimensionId = TutoDimension.tutoDimID;
  • Le premier permet de dire quel est notre WorldChunkManager, donc mettez votre WorldChunkManager.
  • La seconde permet de donner l’ID de la dimension, cet ID doit être le même partout, donc je vous conseille de mettre un “int” et de lui donner votre propre valeur, ça permet aussi d’ajouter un fichier de configuration facilement.
    Vous pouvez aussi ajouter :
this.hasNoSky = false;
this.isHellWorld = false;

Le premier demande s’il y a pas de ciel et la seconde si nous sommes dans un HellWorld (Enfer).
Nous allons ensuite ajouter quelques fonctions bien pratique comme :

    public IChunkProvider createChunkGenerator()
    {
        return new TutoChunkProvider(this.worldObj, this.worldObj.getSeed(), false);
    }

Cette fonction permet de dire quel est notre ChunkProvider.
Il y a aussi cette fonction :

    public String getDimensionName()
    {
        return "tuto";
    }

Le nom de votre dimension.
Et cette fonction :

public String getSaveFolder()
{
    return "tutoDim";
}

Le dossier où est sauvegardé la dimension.
Je vais vous donner encore quelques autre fonctions.

public boolean canRespawnHere()
{
    return false;
}

True si le joueur peut réapparaître dedans sinon vous le mettez en false.

public boolean isSurfaceWorld()
{
    return true;
}

Si notre monde est à la surface.

public boolean getWorldHasVoidParticles()
{
    return true;
}

Si nous avons des particules de vide qui son jouées en fonction de la hauteur du Y (quand le joueur tombe dans le vide).

    public int getHeight()
    {
        return 256;
    }

La hauteur du monde.

public boolean canCoordinateBeSpawn(int par1, int par2)
{
    return this.worldObj.getTopBlock(par1, par2) == Blocks.grass;
}

Le bloc sur lequel le joueur peut apparaître (ne vous inquiétez pas si vous ajoutez un désert, il apparaît sur le sable).

    protected void generateLightBrightnessTable()
    {
        float f = 0.2F;

        for(int i = 0; i <= 15; ++i)
        {
                float f1 = 1.0F - (float)i / 15.0F;
                this.lightBrightnessTable* = (1.0F - f1) / (f1 * 3.0F + 1.0F) * (1.0F - f) + f;
        }
    }

La lumière dans le monde (vous pouvez la modifier).

    public float calculateCelestialAngle(long par1, float par2)
    {
        int j = (int)(par1 % 24000L);
        float f1 = ((float)j + par2) / 24000.0F - 0.25F;

        if(f1 < 0.0F)
        {
                    ++f1;
        }
        if(f1 > 1.0F)
        {
                --f1;
        }
        float f2 = f1;
        f1 = 1.0F - (float)((Math.cos((double)f1 * Math.PI) + 1.0D) / 2.0D);
        f1 = f2 + (f1 - f2) / 3.0F;
        return f1;
    }

Les angles du soleil et de la lune, vous pouvez les modifier.

public int getMoonPhase(long par1)
{
    return (int)(par1 / 24000L % 8L + 8L) % 8;
}

Permet d’obtenir les phases de la lune (vous pouvez les modifier, mais pas trop :P).

public String getWelcomeMessage()
{
    if(this instanceof TutoWorldProvider)
    {
        return "Entering the Tuto Dimension";
    }
    return null;
}

Le message affiché quand le joueur entre dans la dimension.

public String getDepartMessage()
{
    if(this instanceof TutoWorldProvider)
    {
        return "Leaving the Tuto Dimension";
    }
    return null;
}

Le message affiché quand le joueur quitte la dimension.

public double getMovementFactor()
{
    if(this instanceof TutoWorldProvider)
    {       //a changé par votre valeur
        return 10.0;
    }
    return 1.0;
}

Si vous n’ajoutez pas cette fonction rien ne se passera, mais dans notre cas cela correspond au nombre de blocs dans l’overworld par rapport à notre dimension, dans mon cas 10 blocs pour l’overworld fait un bloc dans la dimension (comme le nether, mais avec 8 blocs), si vous n’ajoutez pas cette fonction par défaut nous serons à 1.

    @SideOnly(Side.CLIENT)
    public boolean isSkyColored()
    {
        return true;
    }

Si le ciel a une couleur.

@Override
public Vec3 getSkyColor(Entity cameraEntity, float partialTicks)
{                                 //à modifier pour changer la couleur
    return Vec3.createVectorHelper(2.8, 1.1, 1.7);
}

La couleur du ciel.
Voilà nous avons fini notre classe.