Aurora.Modules.CityBuilder.CityModule.doGenerate C# (CSharp) Method

doGenerate() private method

This method will produce a random city with the central region of the city being specified as a parameter. More parameters need to be made available for this method to produce a better quality city, note for now the minimum area for a city is a 3x3 grid of regions. This code is based on the original C++ version called pixel city.
private doGenerate ( int seed_value ) : bool
seed_value int Random integer seed value.
return bool
        private bool doGenerate(int seed_value)
        {
            int rx, ry;
            //  Based on the initial seed value populate the regions that this shared module
            // is connected to, this means first get a list of the region, determine which
            // region is in the center of all the regions and set this as the hotzone, or
            // central part of the city (this is where the tallest/largest buildings will
            // be created) and will extend out to cover virtually all of the connected
            // regions if desired. No support for aging of the buildings or the city exists
            // yet it is a possible course for the future of this module.

            //  TODO
            //
            //      Validate the details in the configuration file against the settings in
            // the database, otherwise a new user/estate/parcel could be created which will
            // negate any of the security systems that Aurora has in place.

            //  First quick check to see if the module is enabled or not.
            if (!m_fEnabled)
            {
                m_log.Info("[CITY BUILDER]: Disabled, aborting auto generation.");
                return (false);
            }

            m_log.Info("[CITY BUILDER]: Auto generating the city.");

            //  Now we need to ask some basic values for the city generation, we already have
            // the base seed value as this is part of the 'city generate' command, now what
            // about a name, position, size, densities etc. Some of this can be generated
            // based on the seed value, but then, it would need to be confirmed by the user
            // or allow them to change it. TODO move all requested data into the configuration file.
            if (m_UserAccountService == null)
            {
                m_UserAccountService = simulationBase.ApplicationRegistry.RequestModuleInterface<IUserAccountService>();
            }

            //  Decide where the city is to be placed within the server instance.
            int r = CityModule.randomValue(10);

            string regionCount = MainConsole.Instance.CmdPrompt("Region Count ", r.ToString());
            r = Convert.ToInt32(regionCount);
            m_log.InfoFormat("[CITY BUILDER]: City area {0} x {1} regions ", r, r);

            //  Needs to be changed to use the 'defualt' properties and ask about the default
            // estate settings that are to be used, even if it is nothing more than just a
            // confirmation of the settings that are in the configuration file.
            cityName = MainConsole.Instance.CmdPrompt("City Name ", cityName);
            cityOwner = MainConsole.Instance.CmdPrompt("City Owner ", cityOwner);
            m_DefaultUserName = cityOwner;

            //  Make sure that the user and estate information specified in the configuration file
            // have been loaded and the information has either been found or has been created.
            m_DefaultUserAccount = m_UserAccountService.GetUserAccount(UUID.Zero, cityOwner);
            if (m_DefaultUserAccount == null)
            {
                m_log.InfoFormat("[CITY BUILDER]: Creating default account {0}", m_DefaultUserName);
                m_UserAccountService.CreateUser(m_DefaultUserName, Util.Md5Hash(m_DefaultUserPword), m_DefaultUserEmail);
                m_DefaultUserAccount = m_UserAccountService.GetUserAccount(UUID.Zero, m_DefaultUserName);
                cityOwner = m_DefaultUserName;
            }
            else
                m_log.InfoFormat("[CITY BUILDER]: Account found for {0}", m_DefaultUserName);

            // Obtain the scene manager that the server instance is using.
            sceneManager = simulationBase.ApplicationRegistry.RequestModuleInterface<SceneManager>();

            //  Construct the data instance for a city map to hold the total regions in the simulation.
            cityMap = new CityMap();
            citySeed = seed_value;
            cityMap.cityRegions = new Scene[r, r];
            cityMap.cityPlots = new List<BuildingPlot>();
            cityMap.cityBuildings = new List<CityBuilding>();

            //  Construct land and estate data and update to reflect the found user or the newly created one.
            cityLandData = new LandData();
            RegionInfo regionInfo = new RegionInfo();

            regionInfo.RegionID = UUID.Random();

            //  Determine if the default user account as specified in City Builder's configuration file
            // has any predefined estates, if so, just select the first one for now. Perhaps a search of
            // the estates to attempt to find a match to the details from the configuration file.
            EstateConnector = Aurora.DataManager.DataManager.RequestPlugin<IEstateConnector>();
            // Valid estate connection established.
            if (EstateConnector != null)
            {
                //  Valid estate connector, determine if the default user account has any estates.
                List<EstateSettings> estates = EstateConnector.GetEstates(m_DefaultUserAccount.PrincipalID);
                // No estates are found, so construct a new one based on the default estate settings
                // from the configuration file.
                if (estates == null)
                {
                    // No estates present so construct one.
                    m_DefaultEstate = new EstateSettings();

                    m_log.InfoFormat("[CITY BUILDER]: No estates found for user {0}, constructing default estate.", m_DefaultUserAccount.Name);

                    m_DefaultEstate.EstateOwner = m_DefaultUserAccount.PrincipalID;
                    m_DefaultEstate.EstateName = m_DefaultEstateName;
                    m_DefaultEstate.EstatePass = Util.Md5Hash(Util.Md5Hash(m_DefaultEstatePassword));
                    m_DefaultEstate.EstateID = (uint)CityModule.randomValue(1000);

                    regionInfo.EstateSettings = EstateConnector.CreateEstate(m_DefaultEstate, regionInfo.RegionID);
                }
                else
                {
                    //  Estates have been found, select the first estate in the list. No checking is done
                    // against the configuration file settings. TODO validate the estate against the
                    // configuration file.
                    m_DefaultEstate = estates[0];
                    regionInfo.EstateSettings = m_DefaultEstate;
                    m_log.InfoFormat("[CITY BUILDER]: {0} estates found for user {1}, selecting {2}",
                        estates.Count, m_DefaultUserAccount.Name, m_DefaultEstate.EstateName);
                }
            }
            else
            {
                m_log.Info("[CITY BUILDER]: No connection with server.");
                return (false);
            }

            //  Fill in land data for the estate/owner.
            cityLandData.OwnerID = m_DefaultUserAccount.PrincipalID;
            cityLandData.Name = m_DefaultEstateName;
            cityLandData.GlobalID = UUID.Random();
            cityLandData.GroupID = UUID.Zero;

            int regionPort = startPort;

            //  Construct the region.
            regionInfo.RegionSizeX = cityConfig.GetInt("DefaultRegionSize", 256);
            regionInfo.RegionSizeY = regionInfo.RegionSizeX;
            regionInfo.RegionType = "Mainland";
            regionInfo.ObjectCapacity = 100000;
            regionInfo.Startup = StartupType.Normal;
            regionInfo.ScopeID = UUID.Zero;

            IParcelServiceConnector parcelService = simulationBase.ApplicationRegistry.RequestModuleInterface<IParcelServiceConnector>();
            if (parcelService == null)
            {
                m_log.Info("[CITY BUILDER]: Unable to connect to servers parcel service.");
            }

            if (r == 1)
            {
                m_log.Info("[CITY BUILDER]: Single region city.");
                IPAddress address = IPAddress.Parse("0.0.0.0");
                regionInfo.ExternalHostName = Aurora.Framework.Utilities.GetExternalIp();
                regionInfo.FindExternalAutomatically = true;
                regionInfo.InternalEndPoint = new IPEndPoint(address, regionPort++);
                cityLandData.RegionID = regionInfo.RegionID;
                regionInfo.RegionName = "Region00";
                regionInfo.RegionLocX = (int)m_DefaultStartLocation.X;
                regionInfo.RegionLocY = (int)m_DefaultStartLocation.Y;
                if (parcelService != null)
                    parcelService.StoreLandObject(cityLandData);
                if (!createRegion(0, 0, regionInfo))
                {
                    m_log.Info("[CITY BUILDER]: Failed to construct region.");
                    return (false);
                }
            }
            else if (r > 1)
            {
                m_log.Info("[CITY BUILDER]: Multi-region city.");
                m_log.Info("[CITY BUILDER]: Finding external IP, please wait ... ");
                regionInfo.ExternalHostName = Aurora.Framework.Utilities.GetExternalIp();
                if (regionInfo.ExternalHostName.Length <= 0)
                {
                    regionInfo.FindExternalAutomatically = false;
                }
                else
                {
                    m_log.InfoFormat("[CITY BUILDER]: External IP address is {0}", regionInfo.ExternalHostName);
                    regionInfo.FindExternalAutomatically = true;
                }
                //  Construct the regions for the city.
                regionPort = startPort;
                INeighborService neighbours = simulationBase.ApplicationRegistry.RequestModuleInterface<INeighborService>();
                if (neighbours == null)
                {
                    m_log.Info("[CITY BUILDER]: No neighbours.");
                }
                else
                {
                    m_log.Info("[CITY BUILDER]: Neighbours service found.");
                }
                IPAddress address = IPAddress.Parse("0.0.0.0");

                for (rx = 0; rx < r; rx++)
                {
                    for (ry = 0; ry < r; ry++)
                    {
                        regionInfo.InternalEndPoint = new IPEndPoint(address, regionPort++);
                        cityLandData.RegionID = regionInfo.RegionID;
                        regionInfo.RegionName = "Region" + rx + ry;
                        regionInfo.RegionLocX = (int)(m_DefaultStartLocation.X + rx);
                        regionInfo.RegionLocY = (int)(m_DefaultStartLocation.Y + ry);
                        m_log.InfoFormat("[CITY BUILDER]: '{0}' @ {1},{2}, http://{3}/", regionInfo.RegionName,
                            regionInfo.RegionLocX, regionInfo.RegionLocY, regionInfo.InternalEndPoint);
                        if (parcelService != null)
                            parcelService.StoreLandObject(cityLandData);
                        if (!createRegion(rx, ry, regionInfo))
                        {
                            m_log.InfoFormat("[CITY BUILDER]: Failed to construct region at {0},{1}", rx, ry);
                            return (false);
                        }
                        if (neighbours != null)
                        {
                            m_log.Info("[CITY BUILDER]: Informing neighbours.");
                            neighbours.InformOurRegionsOfNewNeighbor(regionInfo);
                        }
                    }
                }
            }

            //  Either generate the terrain or loading from an existing file, DEM for example.
            m_log.Info("[CITY BUILDER]: [TERRAIN]");

            //  Construct the new terrain for each region and pass the height map to it.
            //  For the entire area covered by all of the regions construct a new terrain heightfield for it.
            // Also construct several maps that can be blended together in order to provide a suitablly natural
            // looking terrain which is not too flat or doesn't entirely consist of mountains.
            float[,] terrainMap;
            float[,] hMap1;
            float[,] hMap2;
            float[,] hMap3;
            float[,] hMap4;
            float[] bFactors = new float[4];
            int size = regionInfo.RegionSizeX;
            int y;
            int x;

            terrainMap = new float[ size * r, size * r ];
            hMap1 = new float[ size * r, size * r ];
            hMap2 = new float[ size * r, size * r ];
            hMap3 = new float[ size * r, size * r ];
            hMap4 = new float[ size * r, size * r ];
            //  Set blending factors.
            bFactors[0] = 0.75f;
            bFactors[1] = 0.55f;
            bFactors[2] = 0.35f;
            bFactors[3] = 0.05f;

            //  Generate four layers for the initial height map and then blend them together.
            for (x = 0; x < size; x++)
            {
                for (y = 0; y < size; y++)
                {
                    for (int i = 0; i < r; i++)
                    {
                        for (int j = 0; j < r; j++)
                        {
                            hMap1[i * x, i * y] = Perlin.noise2((float)x+i, (float)y+i);
                            hMap2[i * x, i * y] = Perlin.noise2((float)x, (float)y+i);
                            hMap3[i * x, i * y] = Perlin.noise2((float)x+i, (float)y);
                            hMap4[i * x, i * y] = Perlin.noise2((float)x+j, (float)y+i);
                            terrainMap[i*x, i*y] = (hMap1[i*x, i*y] * bFactors[0]) + (hMap2[i*x, i*y] * bFactors[1]) +
                                (hMap3[i*x, i*y] * bFactors[2]) + (hMap4[i*x, i*y] * bFactors[3]);
                        }
                    }
                }
            }

            //  DEBUG code that will save the resulting terrainMap as a jpeg image.
            m_log.Info("[CITY BUILDER]: Debug, save terrain map (full) as a jpeg image.");
            ManagedImage mImage = new ManagedImage(r * size, r * size, ManagedImage.ImageChannels.Bump);

            //   Find a way of copying the terrainMap array into the newly created image, then save
            // the image to disk.

            m_log.Info("[CITY BUILDER]: Terrain built and blended, tiling and region application.");

            //  Set the height map of each region based on the newly created terrainMap.
            ITerrain terrain = null;
            for (rx = 0; rx < r; rx++)
            {
                for (ry = 0; ry < r; ry++)
                {
                    Scene region = cityMap.cityRegions[rx, ry];
                    ITerrainChannel tChannel = new TerrainChannel(true, region);
                    region.TryRequestModuleInterface<ITerrain>(out terrain);

                    m_log.InfoFormat("[CITY BUILDER]: Region [ {0}, {1} ]", rx, ry);

                    float[,] tile = new float[ size, size ];
                    for (int i = 0; i < size; i++)
                    {
                        for (int j = 0; i < size; j++)
                        {
                            tile[i, j] = terrainMap[(rx * size) + i, (ry * size) + j];
                        }
                    }

                    if (terrain != null & tile!=null)
                        terrain.SetHeights2D(tile);

                    // dispose of the tile now thats its not needed as a new tile is allocated next loop run.
                    tile = null;
                }
            }

            //  Rivers and other waterways. Randomly select a number of rivers for the entire area
            // and place them.
            int rCount = CityModule.randomValue(size / r);
            m_log.InfoFormat("[CITY BUILDER]: River count for entire area {0}", rCount);

            //  From the total number of regions pick a number of regions that will be 'centers'
            // for the entire city, record these in the centralRegions list.
            m_log.Info("[CITY BUILDER]: [CENTERS]");
            //  ( region count * region count ) / 3
            int aNum = CityModule.randomValue((cityMap.cityRegions.GetUpperBound(0) * cityMap.cityRegions.GetUpperBound(1))/3);
            if (aNum == 0)
            {
                aNum = 1;
            }
            m_log.InfoFormat("[CITY BUILDER]: Total regions {0}, selecting {1} regions for centers.", (r*r), aNum );
            int prevRegionX = 0;
            int prevRegionY = 0;
            while ( aNum > 0 )
            {
                int currRegionX = randomValue( cityMap.cityRegions.GetUpperBound(0) ) / 2;
                int currRegionY = randomValue( cityMap.cityRegions.GetUpperBound(1) ) / 2;

                // If the location selected is the same as the previous location try again.
                if (currRegionX == prevRegionX && currRegionY == prevRegionY)
                {
                    aNum--;
                    continue;
                }

                m_log.InfoFormat("[CITY BUILDER]: Region {0}, located {1},{2}", aNum, prevRegionX, prevRegionY);

                try
                {
                    Scene region = cityMap.centralRegions[(prevRegionX * cityMap.cityRegions.GetUpperBound(0)) + prevRegionY];
                    if (region!=null)
                    {
                        cityMap.centralRegions.Add(region);
                    }
                }
                catch
                {
                }
                aNum--;
                prevRegionX = currRegionX;
                prevRegionY = currRegionY;
            }

            m_log.Info("[CITY BUILDER]: [DENSITY]");
            float avgDensity = 0.0f;

            avgDensity += cityDensities[0];
            avgDensity += cityDensities[1];
            avgDensity += cityDensities[2];
            avgDensity += cityDensities[3];
            avgDensity /= 4;

            //  Before ANYTHING else is created construct the transport systems, priority is given
            // to the road network before the rail network, perhaps a configuration option to allow
            // for the prioritisation value of the transport system is possible.
            m_log.Info("[CITY BUILDER]: [FREEWAYS]");

            //  Construct a road system (high speed ~50-70 mph) between and around the city center regions.

            m_log.Info("[CITY BUILDER]: [HIGHWAYS]");

            m_log.Info("[CITY BUILDER]: [STREETS]");

            m_log.Info("[CITY BUILDER]: [RAILWAYS]");

            m_log.InfoFormat("[CITY BUILDER]: [RESIDENTIAL DENSITY] {0}%", cityDensities[0] * 100);
            m_log.InfoFormat("[CITY BUILDER]: [COMMERCIAL DENSITY] {0}%", cityDensities[1] * 100);
            m_log.InfoFormat("[CITY BUILDER]: [CORPORATE DENSITY] {0}%", cityDensities[2] * 100);
            m_log.InfoFormat("[CITY BUILDER]: [INDUSTRIAL DENISTY] {0}%", cityDensities[3] * 100);
            m_log.InfoFormat("[CITY BUILDER]: [AVERAGE DENSITY] {0}%", avgDensity);

            m_log.Info("[CITY BUILDER]: [BLOCKS]");
            m_log.Info("[CITY BUILDER]: [ALLOTMENT PLOTS]");
            m_log.Info("[CITY BUILDER]: [BUILDINGS]");

            return (true);
        }