-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathCityModule.cs
More file actions
1325 lines (1195 loc) · 55.3 KB
/
CityModule.cs
File metadata and controls
1325 lines (1195 loc) · 55.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
*
* City Builder Module for Aurora/OpenSim
*
* $filename :: CityModule.cs
* $author :: Cobra El Diablo
*
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.Xml;
using System.Xml.Serialization;
using log4net;
using Nini;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Imaging;
using Aurora.Services.DataService;
using Aurora.Simulation.Base;
using Aurora.Framework;
using OpenSim;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Framework.Serialization;
using OpenSim.Region;
using OpenSim.Region.CoreModules;
using OpenSim.Region.CoreModules.World.Warp3DMap;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
// This needs to be changed to Aurora.CoreApplicationPlugins, it was working
// but now has revereted to not recognising the namespace despite having added
// the dll as a reference and the project itself as a dependancy of City Builder.
//using Aurora.CoreApplicationPlugins;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
// Add support for new namespace that deals with geospatial data types and processing
// of basic data forms along with the communications and storage of these data types.
using Aurora.Modules.CityBuilder.GeoSpatial.DataTypes;
namespace Aurora.Modules.CityBuilder
{
/// <summary>
/// Defines the data set that is being currently used by City Builder. Valid datasets
/// include a native format (currently in tar archives, but soon to be changed to use
/// MySQL for the extra geospatial support). A GIS data format that allowd for direct
/// use of standardised data for spaces, and a data format suitable for the import of
/// game files from GTA IV.
/// </summary>
public enum DataSetType : int
{
DATASET_TYPE_NULL = -1,
DATASET_TYPE_NATIVE,
DATASET_TYPE_GEOSPATIAL,
DATASET_TYPE_GTAIVPC,
DATASET_TYPE_COUNT
};
/// <summary>
/// The process of generating an enitre city scape from the terrain to the final finishes touches
/// takes on many steps and are presented below.
/// </summary>
public enum GenerationStage : int
{
GENSTAGE_PRESTAGE = -1,
GENSTAGE_INITIALISING,
GENSTAGE_TERRAIN,
GENSTAGE_CENTERS,
GENSTAGE_DENSITY,
GENSTAGE_FREEWAYS,
GENSTAGE_HIGHWAYS,
GENSTAGE_STREETS,
GENSTAGE_RESIDENTIAL_DENSITY,
GENSTAGE_COMMERCIAL_DENSITY,
GENSTAGE_CORPORATE_DENSITY,
GENSTAGE_INDUSTRIAL_DENISTY,
GENSTAGE_BLOCKS,
GENSTAGE_ALLOTMENT_PLOTS,
GENSTAGE_BUILDINGS,
GENSTAGE_COUNT
};
/// <summary>
/// This is the main class that deals with this City Builder Module for Aurora/OpenSim server.
/// </summary>
[Serializable]
public class CityModule : IDataTransferable, IApplicationPlugin, ICityModule
{
#region City Module Constants
public string[] companyNamePrefixes = new string[] {
"i", "Green", "Mega", "Super", "Omni", "e", "Hyper", "Global", "Vital", "Next",
"Pacific", "Metro", "Unity", "G-", "Trans", "Infinity", "Superior", "Monolith",
"Best", "Atlantic", "First", "Union", "National", "Inter National"
};
public string[] companyNames = new string[] {
"Biotic", "Info", "Data", "Solar", "Aerospace", "Motors", "Nano", "Online", "Circuits",
"Energy", "Med", "Robotic", "Exports", "Security", "Systems", "Industrial", "Media",
"Materials", "Foods", "Networks", "Shipping", "Tools", "Medical", "Publishing",
"Enterprise", "Audio", "Health", "Bank", "Imports", "Apparel", "Petroleum", "Studios" };
public string[] companyNameSuffixs = new string[] {
"Corp", "Inc", ".com", "USA", "Ltd", "Net", "Tech", "Labs", "Mfg", "UK", "Unlimited", "One", "LLC" };
#endregion
/// <summary>
/// This section of the module deals with the properties that are specific to the city or to the
/// module itself. Some of the parameters are changeable via the set/get city commands on the console.
/// </summary>
#region Internal Members
private GenerationStage m_CurrentStage = GenerationStage.GENSTAGE_PRESTAGE;
[XmlIgnore]
private UserAccount m_DefaultUserAccount = null;
private string m_DefaultUserName = string.Empty;
private string m_DefaultUserEmail = string.Empty;
[XmlIgnore]
private string m_DefaultUserPword = string.Empty;
[XmlIgnore]
private EstateSettings m_DefaultEstate = null;
private string m_DefaultEstateName = string.Empty;
private string m_DefaultEstateOwner = string.Empty;
[XmlIgnore]
private string m_DefaultEstatePassword = string.Empty;
[XmlIgnore]
private IUserAccountService m_UserAccountService = null;
[XmlIgnore]
private IEstateConnector EstateConnector = null;
private LandData cityLandData = new LandData();
private static bool m_fGridMode = false;
// The start port number for any generated regions, this will increment for every
// region that the plugin produces.
private int startPort = 9500;
// Determines whether the plugin is enabled or not, if disabled then commands issued
// on the command console will be ignored.
[XmlIgnore]
private bool m_fEnabled = false;
// Has the plugin been initialised (installed).
[XmlIgnore]
private bool m_fInitialised = false;
// The random value to use for city generation.
private int citySeed = 0;
// The name of the city, TODO add some for of random name generation for not only the
// city name but also for each region that is created.
private string cityName = string.Empty;
// The owners name (avatar name first/last) that owns the entire region, defaults to
// nothing (same as UUID.Zero) which means it's owned by the server and not an avatar.
private string cityOwner = string.Empty;
[XmlIgnore] // Duplicate of m_DefaultEstateName, todo remove and change all references to m_DefaultEstateName.
private string CityEstate = string.Empty;
// For logging purposes.
[XmlIgnore]
public static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// A map of the city, includes plots, buildings and all regions.
private CityMap cityMap = null;
// Configuration for the plugin.
[XmlIgnore]
private IConfig cityConfig = null;
// Configuration source from Aurora.
[XmlIgnore]
private IConfigSource configSource = null;
// Scene manager for region creation.
[XmlIgnore]
public SceneManager sceneManager = null;
[XmlIgnore]
private SceneGraph sceneGraph = null;
// Simulation base from Aurora.
[XmlIgnore]
private ISimulationBase simulationBase = null;
// Densities for various parts of the city, residential, commercial, industrial etc.
private List<float> cityDensities = new List<float>();
private Vector2 m_DefaultStartLocation = new Vector2(9500, 9500);
private List<System.Threading.Thread> m_ThreadPool = new List<System.Threading.Thread>();
#endregion
/// <summary>
/// Internal methods private to the City Module.
/// </summary>
/// <param name="range"></param>
/// <returns></returns>
#region Internal Methods
/// <summary>
///
/// </summary>
/// <param name="range"></param>
/// <returns></returns>
private void InstallModule()
{
//
// CONSOLE COMMAND INTERFACE
//
// Adds various commands to the server's console to allow for direct manipulation
// of the module and it's internal properties.
//
// Add a command city set to allow for the direct setting of the properties of the module.
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city set", "Sets an internal property to the given value.",
"Allows for the manipulation of the internal property values in the City Builder Module",
cmdSetParameter);
// Add a command city get to allow for the display of the current value for a module property.
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city generate", "Auto generate a city from given parameters.",
"This command will generate a city across an entire region or server instance of regions " +
"based on the given parameters", cmdGenerate);
//export
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city export",
"Export the current settings for this city to a file.",
"Exports current module settings for the city for each region that is part of the city",
cmdExport);
//import
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city import",
"Imports a previously saved city definition file, or import from GTA IV PC",
"Imports the settings required for a given city and recreate it inworld on one or more regions",
cmdImport);
//help
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city help",
"Display help information",
"Display some help information about the use of the City Builder module from the command console",
cmdHelp);
//reset
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city reset",
"Reset the internal properties of the module to the ones used during development.",
"Reset properties to 'Cobra ElDiablo' (City Owner), 'Liberty City' (City Name), Lave (City Region [SINGLE])",
cmdReset);
// enable
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city enable",
"Enables the City Module.", "Allows for the module to be re-enabled after changing internal properties",
cmdEnable);
// disable
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand("city", true, "city disable",
"Disables the City Module.", "Allows for the module to be disabled before altering internal properties.",
cmdDisable);
// add a command 'city info' to the main console.
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city info", "Displays information from the City Builder Module.",
"Displays information about the current parameters from the city, like the number of buildings.",
cmdInfo);
// add a 'city building' command (note has subcommands)
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city building", "City building interface",
"Allow for the manipulation of buildings directly.",
cmdBuilding);
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city backup", "Backup the city instance to disk/database.",
"Allows for the generated city to be backed up to disk or database.",
cmdBackup);
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city restore", "Restores a city from disk or database.",
"Allows for previously backed up cities to be restored from disk or database.",
cmdRestore);
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city list", "List all known cities",
"Displays a list of all cities present on disk or database.",
cmdList);
OpenSim.Framework.MainConsole.Instance.Commands.AddCommand(
"city", true, "city builder", "", "Opens a GUI editor for city generation parameter tweaking.",
cmdCityBuilder);
m_fInitialised = true;
}
/// <summary>
///
/// </summary>
/// <param name="range"></param>
/// <returns></returns>
public static int randomValue(int range)
{
Random rnd = new Random();
int r = 0;
r = rnd.Next(range);
return r;
}
/// <summary>
///
/// </summary>
private void doBackup()
{
}
/// <summary>
///
/// </summary>
private void doRestore()
{
}
/// <summary>
///
/// </summary>
private void doList()
{
}
/// <summary>
///
/// </summary>
/// <param name="filePath"></param>
/// <param name="GTAIV"></param>
/// <returns></returns>
private bool doExport(string filePath, DataSetType data_type )
{
// Initial stage for the export/import functionality is the ability to export/import
// the data correctly for the current city, uses OSDMap. For now the GTAIV flag is
// ignored, this needs to be changed to allow for internal, GTA and GIS data sets to
// be used for cities.
if (filePath == null || data_type == DataSetType.DATASET_TYPE_NULL)
{
return (false);
}
// Make sure the file is not present on the destination.
// ???
// First stage use a TarArchiveWriter in conjunction with OSDMap to write a file
// containing all the information needed about the city and regions to be able to
// import it in when the server is shutdown and restarted.
System.IO.MemoryStream data = new System.IO.MemoryStream(1024);
TarArchiveWriter tarArchive = null;
// Construct the archive before writing it to the destination.
tarArchive = new TarArchiveWriter(data);
tarArchive.WriteDir("citybuilder/");
tarArchive.WriteFile(filePath, data.GetBuffer());
return (false);
}
/// <summary>
///
/// </summary>
/// <param name="filePath"></param>
/// <param name="GTAIV"></param>
/// <returns></returns>
private bool doImport(string filePath, DataSetType data_type )
{
// Import the file from the specified file path. For now the GTAIV flag is
// ignored.
m_log.InfoFormat("[CITY BUILDER]: Importing from {0}", filePath);
return (false);
}
/// <summary>
///
/// </summary>
/// <param name="x1"></param>
/// <param name="z1"></param>
/// <param name="direction"></param>
private void doLightStrip(int x1, int z1, int direction)
{
;
}
/// <summary>
///
/// </summary>
/// <param name="x1"></param>
/// <param name="?"></param>
/// <param name="width"></param>
/// <param name="height"></param>
private void doRoad(int x1, int y1, int width, int depth)
{
int lanes;
int divider;
int sidewalk;
if (width > depth)
lanes = depth;
else
lanes = width;
if (lanes < 4)
return;
bool odd = false;
int i = (lanes % 2);
if (i > 0) odd = true;
if (odd)//lanes % 2)
{
lanes--;
divider = 1;
}
else
divider = 0;
sidewalk = 2;// MAX(2, (lanes - 10));
lanes -= sidewalk;
sidewalk /= 2;
lanes /= 2;
cityMap.ClaimPlot(cityMap.MakePlot(x1, y1, width, depth, PlotClaimType.CLAIM_NONE));
if (width > depth)
{
cityMap.ClaimPlot(cityMap.MakePlot(x1, y1 + sidewalk, width, lanes, PlotClaimType.CLAIM_TRANSPORT));
cityMap.ClaimPlot(cityMap.MakePlot(x1, y1 + sidewalk + lanes + divider, width, lanes, PlotClaimType.CLAIM_TRANSPORT));
}
else
{
cityMap.ClaimPlot(cityMap.MakePlot(x1 + sidewalk, y1, lanes, depth, PlotClaimType.CLAIM_TRANSPORT));
cityMap.ClaimPlot(cityMap.MakePlot(x1 + sidewalk + lanes + divider, y1, lanes, depth, PlotClaimType.CLAIM_TRANSPORT));
}
}
/// <summary>
///
/// </summary>
/// <param name="plot"></param>
private void doBuilding()
{
// Construct a random building and place it into the buildings list.
CityBuilding building;
BuildingType type = BuildingType.BUILDING_CIVIL | BuildingType.BUILDING_GENERAL;
BuildingFlags flags = BuildingFlags.BUILDING_FLAG_ACOND | BuildingFlags.BUILDING_FLAG_LIGHTS |
BuildingFlags.BUILDING_FLAG_LOGO | BuildingFlags.BUILDING_FLAG_TRIM;
BuildingPlot plot = new BuildingPlot();
plot.XPos = randomValue(256) / 4;
plot.YPos = randomValue(256) / 4;
plot.Width = (byte)randomValue(10);
plot.Depth = (byte)randomValue(10);
plot.PlotClaimType = PlotClaimType.CLAIM_BUILDING | PlotClaimType.CLAIM_COMPLEX;
building = new CityBuilding(type, plot, flags, UUID.Zero,cityMap.centralRegions[0],"Building");
}
/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public bool createRegion(int x, int y, RegionInfo regionInfo)
{
/*
* Construct a region for the given position in the city.
*/
// Validate the supplied parameters and internal ones.
// If a region already exists at the specified position just exit.
if (!m_fEnabled || !m_fInitialised || cityConfig==null)
{
m_log.Info("[CITY BUILDER]: FAIL! not enabled, initialised or no configuration");
return (false);
}
if (cityMap.Equals(null) || cityMap.cityRegions.Equals(null))
return (false);
// Now ask the scene manager to construct the region.
if (!sceneManager.Equals(null))
{
IScene scene = (IScene)cityMap.cityRegions[x, y];
sceneManager.CreateRegion(regionInfo, out scene);
}
else
{
m_log.Info("[CITY BUILDER]: NO SCENE MANAGER");
return (false);
}
// Job done, exit with OK.
return (true);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="seed_value">Random integer seed value.</param>
/// <returns>true / false indicator of success or failure.</returns>
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);
}
#endregion
#region Public Properties
public GenerationStage GenerationStage
{
get { return (m_CurrentStage); }
}
#endregion
/// <summary>
/// This section deals with the inherited interface IApplicationPlugin, note some methods
/// are duplicates of ones in the ISharedRegionModule interface region.
/// </summary>
#region IApplicationPlugin Region
/// <summary>
/// This method is during the startup of the Aurora server, It is called just after the
/// HTTP server has started and before any shared modules or regions have been loaded.
/// </summary>
/// <param name="config">Configuration parameter stream.</param>
public void Initialize(ISimulationBase openSim)
{
// Display a startup message, save the passed 'server instance?', obtain the
// scene manager and scene graph for this server instance and obtain the interface
// used to access region information, regardless of whether the storage method is
// file, web or MySQL based in reality. Finally call an internal method that installs
// the command console commands and alters some internal properties, indicating that
// the module is loaded, enabled and ready for use.
m_log.Info("[CITY BUILDER]: Version 0.0.0.10 ");
// Store the supplied simulation base to for future use.
simulationBase = openSim;
// Store the configuration source (I presume all contents of all ini files).
configSource = simulationBase.ConfigSource;
// Store the configuration section specifically used by City Builder.
cityConfig = configSource.Configs["CityBuilder"];
// Obtain the default user account service, do the same for the estate/parcel too.
m_UserAccountService = simulationBase.ApplicationRegistry.RequestModuleInterface<IUserAccountService>();
// Register the ICityModule interface with the simulation base.
simulationBase.ApplicationRegistry.RegisterModuleInterface<ICityModule>(this);
m_log.Info("[CITY BUILDER]: ICityModule interface registered with simulation base.");
// If we have a configuration source for City Builder then set the specified internal properties else default them.
if (cityConfig != null)
{
// Configuration file is present or it is included within one of the other configuration
// file that control aurora obtain the specified values or use hardcoded defaults.
m_log.Info("[CITY BUILDER]: Configuration found, stored.");
// Construct Land data to be used for the entire city and any occupied regions.
m_DefaultUserAccount = null;
// Construct the estate settings for the city.
m_DefaultEstate = null;
startPort = cityConfig.GetInt("DefaultStartPort", startPort);
m_fEnabled = cityConfig.GetBoolean("Enabled", m_fEnabled);
m_fInitialised = false;
citySeed = CityModule.randomValue(257);
cityName = cityConfig.GetString("DefaultCityName", "CityVille");
cityOwner = cityConfig.GetString("DefaultCityOwner", "Cobra ElDiablo");
m_DefaultUserName = cityOwner;
m_DefaultUserEmail = cityConfig.GetString("DefaultUserEmail", "");
m_DefaultUserPword = cityConfig.GetString("DefaultUserPassword", "");
CityEstate = cityConfig.GetString("DefaultCityEstate", "Liquid Silicon Developments");
m_DefaultEstateOwner = cityOwner;
m_DefaultEstatePassword = cityConfig.GetString("DefaultEstatePassword", "");
cityDensities = new List<float>();
m_DefaultStartLocation = new Vector2(9500, 9500);
startPort = cityConfig.GetInt("DefaultStartPort", 9500);
}
else
{
m_log.Info("[CITY BUILDER]: No configuration data found.");
m_DefaultUserAccount = null;
m_DefaultEstate = null;
m_fEnabled = false;
m_fInitialised = false;
citySeed = CityModule.randomValue(257);
cityName = string.Empty;
cityOwner = string.Empty;
CityEstate = string.Empty;
// Configuration for the plugin.
// Configuration source from Aurora.
cityConfig = new ConfigBase("CityBuilder",configSource);
cityDensities = new List<float>();
m_DefaultStartLocation = new Vector2(9500, 9500);
// automatically disable the module if a configuration is not found. You can
// manually enable the module and then set its internal properties before using
// it via the server command console prompt.
m_fEnabled = false;
}
cityDensities.Add(0.85f);
cityDensities.Add(0.75f);
cityDensities.Add(0.65f);
cityDensities.Add(0.45f);
// Install the module, does not alter the enabled flag! This allows for the plugin
// to install some commands for the main servers console but prevents any use of
// the plugin until the internal properties are set correctly.
InstallModule();
}
/// <summary>
///
/// </summary>
/// <param name="config"></param>
public void ReloadConfiguration(IConfigSource config)
{
m_log.Info("CityModule.ReloadConfiguration");
}
/// <summary>
///
/// </summary>
public void Start()
{
m_log.Info("CityModule.Start");
}
/// <summary>
///
/// </summary>
public void PostStart()
{
m_log.Info("CityModule.PostStart");
}
/// <summary>
///
/// </summary>
public void Close()
{
m_log.Info("[CITY BUILDER]: Terminating.");
m_fEnabled = false;
}
/// <summary>