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);
}