public static void TestAssetStorageSimpleFolderTree_TryGetAsset_Unknown_ReturnsFalse()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            Assert.False(localStorage.TryGetAsset(Guid.NewGuid(), out var asset));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_SingleFilter_OneMatch_OneMismatch_Correct()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId1 = Guid.NewGuid();
            var assetId2 = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id    = assetId1,
                Local = false,
            });
            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id    = assetId2,
                Local = true,
            });

            localStorage.PurgeAll(new List <AssetFilter> {
                new AssetFilter {
                    LocalFilter = true,
                }
            });

            Assert.IsTrue(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, assetId1));
            Assert.IsFalse(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, assetId2));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_Null_EmptyLocalStorage_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            Assert.DoesNotThrow(() => localStorage.PurgeAll(null));
        }
        public static void TestAssetStorageSimpleFolderTree_TryGetAsset_EmptyLocalStorage_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            Assert.DoesNotThrow(() => localStorage.TryGetAsset(Guid.NewGuid(), out var asset));
        }
        public static void TestAssetStorageSimpleFolderTree_Purge_Unknown_AssetNotFoundException()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            Assert.Throws <AssetNotFoundException>(() => localStorage.Purge(assetId));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_Single_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id = Guid.NewGuid(),
            };

            Assert.DoesNotThrow(() => localStorage.StoreAsset(testAsset));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_SingleFilter_EmptyLocalStorage_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            Assert.DoesNotThrow(() => localStorage.PurgeAll(new List <AssetFilter> {
                new AssetFilter {
                    LocalFilter = true,
                }
            }));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_NoLocalStorage_DoesntThrow()
        {
            var server       = Substitute.For <IAssetServer>();
            var config       = new ChattelConfiguration(server);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id = Guid.NewGuid(),
            };

            Assert.DoesNotThrow(() => localStorage.StoreAsset(testAsset));
        }
        public static void TestAssetStorageSimpleFolderTree_TryGetAsset_Known_ReturnsTrue()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id = assetId,
            });

            Assert.True(localStorage.TryGetAsset(assetId, out var asset));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_Single_CreatesAsset()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id = Guid.NewGuid(),
            };

            localStorage.StoreAsset(testAsset);

            Assert.IsTrue(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, testAsset.Id));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_Null_NonEmptyLocalStorage_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id = assetId,
            });

            Assert.DoesNotThrow(() => localStorage.PurgeAll(null));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_SerialDuplicate_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id          = Guid.NewGuid(),
                Description = "7nwtzcv84w78",
            };

            localStorage.StoreAsset(testAsset);

            Assert.DoesNotThrow(() => localStorage.StoreAsset(testAsset));
        }
        public static void TestAssetStorageSimpleFolderTree_TryGetAsset_Known_OutEqualAssets()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id = Guid.NewGuid(),
            };

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, testAsset);

            localStorage.TryGetAsset(testAsset.Id, out var asset);
            Assert.AreEqual(testAsset, asset);
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_NoLocalStorage_DoesntExist()
        {
            var server       = Substitute.For <IAssetServer>();
            var config       = new ChattelConfiguration(server);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id = Guid.NewGuid(),
            };

            localStorage.StoreAsset(testAsset);

            Assert.IsFalse(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, testAsset.Id));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_Single_AssetStoredCorrectly()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var testAsset = new StratusAsset {
                Id          = Guid.NewGuid(),
                Description = "7nwtzcv84w78",
            };

            localStorage.StoreAsset(testAsset);

            Assert.AreEqual(testAsset, GetLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, testAsset.Id));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_Null_NonEmptyLocalStorage_RemovesEntry()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id = assetId,
            });

            localStorage.PurgeAll(null);

            Assert.IsFalse(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, assetId));
        }
        public static void TestAssetStorageSimpleFolderTree_Purge_Known_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id = assetId,
            });

            Assert.That(LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, assetId), "Failed to prep local storage file!");

            Assert.DoesNotThrow(() => localStorage.Purge(assetId));
        }
Example #18
0
        public static void TestChattelReader_Ctor_BasicCfg_LSTree_True_PurgesLocalStorage()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);
            var assetId      = Guid.NewGuid();

            TestAssetStorageSimpleFolderTree.CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id = assetId,
            });

#pragma warning disable RECS0026 // Possible unassigned object created by 'new'
            new ChattelReader(config, localStorage, true);
#pragma warning restore RECS0026 // Possible unassigned object created by 'new'

            Assert.False(TestAssetStorageSimpleFolderTree.LocalStorageEntryExists(LOCAL_STORAGE_DIR_INFO, assetId));
        }
        public static void TestAssetStorageSimpleFolderTree_PurgeAll_SingleFilter_Nonmatch_NonEmptyLocalStorage_DoesntThrow()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            CreateLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, new StratusAsset {
                Id    = assetId,
                Local = false,
            });

            Assert.DoesNotThrow(() => localStorage.PurgeAll(new List <AssetFilter> {
                new AssetFilter {
                    LocalFilter = true,
                }
            }));
        }
        public static void TestAssetStorageSimpleFolderTree_StoreAsset_SerialDuplicate_DoesntOverwrite()
        {
            var config       = new ChattelConfiguration(LOCAL_STORAGE_DIR_INFO.FullName);
            var localStorage = new AssetStorageSimpleFolderTree(config);

            var assetId = Guid.NewGuid();

            var testAsset1 = new StratusAsset {
                Id          = assetId,
                Description = "7nwtzcv84w78",
            };

            var testAsset2 = new StratusAsset {
                Id          = assetId,
                Description = "f4fn983984",
            };

            localStorage.StoreAsset(testAsset1);
            localStorage.StoreAsset(testAsset2);

            Assert.AreEqual(testAsset1, GetLocalStorageEntry(LOCAL_STORAGE_DIR_INFO, testAsset2.Id));
        }
        public static int Main(string[] args)
        {
            // First line, hook the appdomain to the crash reporter
#pragma warning disable RECS0164 // Explicit delegate creation expression is redundant
            // Analysis disable once RedundantDelegateCreation // The "new" is required.
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
#pragma warning restore RECS0164 // Explicit delegate creation expression is redundant

            var watch = System.Diagnostics.Stopwatch.StartNew();

            // Add the arguments supplied when running the application to the configuration
            var configSource = new ArgvConfigSource(args);
            _configSource = configSource;

            // Commandline switches
            configSource.AddSwitch("Startup", "inifile");
            configSource.AddSwitch("Startup", "logconfig");
            configSource.AddSwitch("Startup", "MaxParallelism", "p");
            configSource.AddSwitch("Startup", "ServerMode");

            var startupConfig = _configSource.Configs["Startup"];

            // TODO: var pidFileManager = new PIDFileManager(startupConfig.GetString("pidfile", string.Empty));

            // Configure Log4Net
            {
                var logConfigFile = startupConfig.GetString("logconfig", string.Empty);
                if (string.IsNullOrEmpty(logConfigFile))
                {
                    XmlConfigurator.Configure();
                    LogBootMessage();
                    LOG.Info("Configured log4net using ./Anaximander.exe.config as the default.");
                }
                else
                {
                    XmlConfigurator.Configure(new FileInfo(logConfigFile));
                    LogBootMessage();
                    LOG.Info($"Configured log4net using \"{logConfigFile}\" as configuration file.");
                }
            }

            // Configure nIni aliases and localles
            Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", true);

            configSource.Alias.AddAlias("On", true);
            configSource.Alias.AddAlias("Off", false);
            configSource.Alias.AddAlias("True", true);
            configSource.Alias.AddAlias("False", false);
            configSource.Alias.AddAlias("Yes", true);
            configSource.Alias.AddAlias("No", false);

            // Read in the ini file
            ReadConfigurationFromINI(configSource);

            var configRead        = configSource.Configs["AssetsRead"];
            var serversRead       = GetServers(configSource, configRead, _assetServersByName);
            var chattelConfigRead = GetConfig(configRead, serversRead);

            // Create an IPC wait handle with a unique identifier.
            var serverMode  = startupConfig.GetBoolean("ServerMode", Constants.KeepRunningDefault);
            var createdNew  = true;
            var waitHandle  = serverMode ? new EventWaitHandle(false, EventResetMode.AutoReset, "4d1ede7a-7f81-4934-bc59-f4fe10396408", out createdNew) : null;
            var serverState = serverMode ? ServerState.Starting : ServerState.Ignored;

            // If the handle was already there, inform the user and die.
            if (serverState == ServerState.Starting && !createdNew)
            {
                LOG.Error("Server process alredy started, please stop that server first.");
                return(2);
            }

            LOG.Info($"Configured for max degree of parallelism of {startupConfig.GetInt("MaxParallelism", Constants.MaxDegreeParallism)}");

            var readerLocalStorage = new AssetStorageSimpleFolderTree(chattelConfigRead);

            var chattelReader = new ChattelReader(chattelConfigRead, readerLocalStorage);             // TODO: add purge flag to CLI
            Texture.Initialize(chattelReader);

            watch.Stop();
            LOG.Info($"Read configuration in {watch.ElapsedMilliseconds} ms.");
            watch.Restart();

            // Load the RDB map
            try {
                _rdbMap = new RDBMap(configSource);
            }
            catch (DatabaseException e) {
                LOG.Error($"Unable to continue without database connection. Aborting.", e);

                return(1);
            }

            watch.Stop();
            LOG.Info($"Loaded region DB in {watch.ElapsedMilliseconds} ms for a total of {_rdbMap.GetRegionCount()} regions, resulting in an average of {(float)watch.ElapsedMilliseconds / _rdbMap.GetRegionCount()} ms / region.");
            watch.Restart();

            /* Issues to watch for:
             * Region delete - The DBA will need to actually remove the estate record to cause a map tile delete.
             * TODO: Tile image read during write - The web server could attempt to read a file while the file is being written.
             *  - Possible solution: write to a random filename then try { mv rndname to finalname with overwrite } catch { try again later for a max of N times }
             *    This should provide as much atomicity as possible, and allow anything that's blocking access to be bypassed via time delay. Needs to just fail under exceptions that indicate always-fail conditions.
             */

            {
                LOG.Debug("Initializing writer and generator.");
                _tileWriter    = new TileImageWriter(configSource);
                _tileGenerator = new TileGenerator(configSource);

                LOG.Debug("Writing ocean tile.");
                // Generate & replace ocean tile
                using (var ocean_tile = _tileGenerator.GenerateOceanTile()) {
                    _tileWriter.WriteOceanTile(ocean_tile.Bitmap);
                }

                LOG.Debug("Generating a full batch of region tiles.");
                // Generate region tiles - all existing are nearly guaranteed to be out of date.
                var options = new ParallelOptions {
                    MaxDegreeOfParallelism = startupConfig.GetInt("MaxParallelism", Constants.MaxDegreeParallism)
                };                                                                                                                                                   // -1 means full parallel.  1 means non-parallel.
                Parallel.ForEach(_rdbMap.GetRegionUUIDs(), options, (region_id) => {
                    var oldPriority = Thread.CurrentThread.Priority;

                    try {
                        Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

                        UpdateRegionTile(region_id);
                    }
                    finally {
                        Thread.CurrentThread.Priority = oldPriority;
                    }
                });

                watch.Stop();
                LOG.Info($"Created full res map tiles in {watch.ElapsedMilliseconds} ms all regions with known locations, resulting in an average of {(float)watch.ElapsedMilliseconds / _rdbMap.GetRegionCount()} ms / region.");
                watch.Restart();


                // Generate zoom level tiles.
                // Just quickly build the tile tree so that lookups of the super tiles can be done.

                var superGen = new SuperTileGenerator(configSource, _rdbMap);

                superGen.PreloadTileTrees(_rdbMap.GetRegionUUIDs());

                watch.Stop();
                LOG.Info($"Preloaded tile tree in {watch.ElapsedMilliseconds} ms.");
                watch.Restart();


                // Remove all tiles that do not have a corresponding entry in the map.
                _tileWriter.RemoveDeadTiles(_rdbMap, superGen.AllNodesById);

                watch.Stop();
                LOG.Info($"Removed all old tiles in {watch.ElapsedMilliseconds} ms.");
                watch.Restart();


                // Actually generate the zoom level tiles.
                superGen.GeneratePreloadedTree();

                watch.Stop();
                LOG.Info($"Created all super tiles in {watch.ElapsedMilliseconds} ms.");
            }

            // Activate server process
            if (serverState == ServerState.Starting)
            {
                System.Console.CancelKeyPress += (sender, cargs) => {
                    cargs.Cancel = true;
                    waitHandle.Set();
                };

                serverState = ServerState.Running;

                var server_config = configSource.Configs["Server"];

                var domain = server_config?.GetString("UseSSL", Constants.ServerDomain) ?? Constants.ServerDomain;
                var port   = (uint)(server_config?.GetInt("UseSSL", Constants.ServerPort) ?? Constants.ServerPort);
                var useSSL = server_config?.GetBoolean("UseSSL", Constants.ServerUseSSL) ?? Constants.ServerUseSSL;

                var protocol = useSSL ? "https" : "http";
                LOG.Info($"Activating server on '{protocol}://{domain}:{port}', listening for region updates.");

                RestApi.RestAPI.StartHost(
                    UpdateRegionDelegate,
                    MapRulesDelegate,
                    CheckAPIKeyDelegate,
                    domain,
                    port,
                    useSSL
                    );

                waitHandle.WaitOne();
                serverState = ServerState.Stopping;
            }

            // I don't care what's still connected or keeping things running, it's time to die!
            Environment.Exit(0);
            return(0);
        }