Exemple #1
0
        /// <summary>
        /// Generates all SlippyMap tiles for a given area and zoom level, and saves them to the database.
        /// </summary>
        /// <param name="buffered">the GeoArea to generate tiles for</param>
        /// <param name="zoomLevel">the zoom level to generate tiles at.</param>
        public static void PregenSlippyMapTilesForArea(GeoArea buffered, int zoomLevel)
        {
            //There is a very similar function for this in Standalone.cs, but this one writes back to the main DB.
            var db = new PraxisContext();

            db.ChangeTracker.AutoDetectChangesEnabled = false;
            var intersectCheck = Converters.GeoAreaToPolygon(buffered);

            //start drawing maptiles and sorting out data.
            var swCornerLon = Converters.GetSlippyXFromLon(intersectCheck.EnvelopeInternal.MinX, zoomLevel);
            var neCornerLon = Converters.GetSlippyXFromLon(intersectCheck.EnvelopeInternal.MaxX, zoomLevel);
            var swCornerLat = Converters.GetSlippyYFromLat(intersectCheck.EnvelopeInternal.MinY, zoomLevel);
            var neCornerLat = Converters.GetSlippyYFromLat(intersectCheck.EnvelopeInternal.MaxY, zoomLevel);

            //declare how many map tiles will be drawn
            var xTiles     = neCornerLon - swCornerLon + 1;
            var yTiles     = swCornerLat - neCornerLat + 1;
            var totalTiles = xTiles * yTiles;

            Log.WriteLog("Starting processing " + totalTiles + " maptiles for zoom level " + zoomLevel);
            long mapTileCounter = 0;

            System.Diagnostics.Stopwatch progressTimer = new System.Diagnostics.Stopwatch();
            progressTimer.Start();

            //foreach (var y in yCoords)
            for (var y = neCornerLat; y <= swCornerLat; y++)
            {
                //Make a collision box for just this row of Cell8s, and send the loop below just the list of things that might be relevant.
                //Add a Cell8 buffer space so all elements are loaded and drawn without needing to loop through the entire area.
                GeoArea thisRow = new GeoArea(Converters.SlippyYToLat(y + 1, zoomLevel) - ConstantValues.resolutionCell8,
                                              Converters.SlippyXToLon(swCornerLon, zoomLevel) - ConstantValues.resolutionCell8,
                                              Converters.SlippyYToLat(y, zoomLevel) + ConstantValues.resolutionCell8,
                                              Converters.SlippyXToLon(neCornerLon, zoomLevel) + resolutionCell8);
                var row         = Converters.GeoAreaToPolygon(thisRow);
                var rowList     = GetPlaces(thisRow);
                var tilesToSave = new ConcurrentBag <SlippyMapTile>();

                Parallel.For(swCornerLon, neCornerLon + 1, (x) =>
                {
                    //make map tile.
                    var info     = new ImageStats(zoomLevel, x, y, IMapTiles.SlippyTileSizeSquare);
                    var acheck   = Converters.GeoAreaToPolygon(info.area);                            //this is faster than using a PreparedPolygon in testing, which was unexpected.
                    var areaList = rowList.Where(a => acheck.Intersects(a.ElementGeometry)).ToList(); //This one is for the maptile

                    var tile = MapTiles.DrawAreaAtSize(info, areaList);
                    tilesToSave.Add(new SlippyMapTile()
                    {
                        TileData = tile, Values = x + "|" + y + "|" + zoomLevel, ExpireOn = DateTime.Now.AddDays(3650), AreaCovered = Converters.GeoAreaToPolygon(info.area), StyleSet = "mapTiles"
                    });

                    mapTileCounter++;
                });
                db.SlippyMapTiles.AddRange(tilesToSave);
                db.SaveChanges();
                Log.WriteLog(mapTileCounter + " tiles processed, " + Math.Round(((mapTileCounter / (double)totalTiles * 100)), 2) + "% complete");
            }//);
            progressTimer.Stop();
            Log.WriteLog("Zoom " + zoomLevel + " map tiles drawn in " + progressTimer.Elapsed.ToString());
        }
Exemple #2
0
        /// <summary>
        /// Saves a key/value pair to a given map element. Will reject a pair containing a player's deviceId in the database.
        /// </summary>
        /// <param name="elementId">the Guid exposed to clients to identify the map element.</param>
        /// <param name="key">The key to save to the database for the map element.</param>
        /// <param name="value">The value to save with the key.</param>
        /// <param name="expiration">If not null, expire the data in this many seconds from now.</param>
        /// <returns>true if data was saved, false if data was not.</returns>
        public static bool SetPlaceData(Guid elementId, string key, string value, double?expiration = null)
        {
            var db = new PraxisContext();

            if (db.PlayerData.Any(p => p.DeviceID == key || p.DeviceID == value))
            {
                return(false);
            }

            var row = db.PlaceGameData.Include(p => p.Place).FirstOrDefault(p => p.Place.PrivacyId == elementId && p.DataKey == key);

            if (row == null)
            {
                var sourceItem = db.Places.First(p => p.PrivacyId == elementId);
                row         = new DbTables.PlaceGameData();
                row.DataKey = key;
                row.Place   = sourceItem;
                db.PlaceGameData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }
            row.IvData    = null;
            row.DataValue = value.ToByteArrayUTF8();
            return(db.SaveChanges() == 1);
        }
Exemple #3
0
        /// <summary>
        /// Saves a key/value pair to a given player's DeviceID with a password.
        /// </summary>
        /// <param name="playerId">the unique ID for the player, expected to be their unique device ID</param>
        /// <param name="key">The key to save to the database. Keys are unique, and you cannot have multiples of the same key.</param>
        /// <param name="value">The value to save with the key.</param>
        /// <param name="password"></param>
        /// <param name="expiration">If not null, expire this data in this many seconds from now.</param>
        /// <returns>true if data was saved, false if data was not.</returns>
        public static bool SetSecurePlayerData(string playerId, string key, byte[] value, string password, double?expiration = null)
        {
            var encryptedValue = EncryptValue(value, password, out byte[] IVs);

            var db  = new PraxisContext();
            var row = db.PlayerData.FirstOrDefault(p => p.DeviceID == playerId && p.DataKey == key);

            if (row == null)
            {
                row          = new DbTables.PlayerData();
                row.DataKey  = key;
                row.DeviceID = playerId;
                db.PlayerData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }
            row.IvData    = IVs;
            row.DataValue = encryptedValue;
            return(db.SaveChanges() == 1);
        }
Exemple #4
0
        /// <summary>
        /// Saves a key/value pair to a given map element with the given password
        /// </summary>
        /// <param name="elementId">the Guid exposed to clients to identify the map element.</param>
        /// <param name="key">The key to save to the database for the map element.</param>
        /// <param name="value">The value to save with the key.</param>
        /// <param name="password">The password to encrypt the value with.</param>
        /// <param name="expiration">If not null, expire this data in this many seconds from now.</param>
        /// <returns>true if data was saved, false if data was not.</returns>
        public static bool SetSecurePlaceData(Guid elementId, string key, byte[] value, string password, double?expiration = null)
        {
            byte[] encryptedValue = EncryptValue(value, password, out byte[] IVs);
            var    db             = new PraxisContext();

            var row = db.PlaceGameData.Include(p => p.Place).FirstOrDefault(p => p.Place.PrivacyId == elementId && p.DataKey == key);

            if (row == null)
            {
                var sourceItem = db.Places.First(p => p.PrivacyId == elementId);
                row         = new DbTables.PlaceGameData();
                row.DataKey = key;
                row.Place   = sourceItem;
                db.PlaceGameData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }
            row.IvData    = IVs;
            row.DataValue = encryptedValue;
            return(db.SaveChanges() == 1);
        }
Exemple #5
0
        public static bool SetSecureAreaData(string plusCode, string key, byte[] value, string password, double?expiration = null)
        {
            var db = new PraxisContext();

            byte[] encryptedValue = EncryptValue(value, password, out byte[] IVs);

            var row = db.AreaGameData.FirstOrDefault(p => p.PlusCode == plusCode && p.DataKey == key);

            if (row == null)
            {
                row              = new DbTables.AreaGameData();
                row.DataKey      = key;
                row.PlusCode     = plusCode;
                row.GeoAreaIndex = Converters.GeoAreaToPolygon(OpenLocationCode.DecodeValid(plusCode.ToUpper()));
                db.AreaGameData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }

            row.DataValue = encryptedValue;
            row.IvData    = IVs;
            return(db.SaveChanges() == 1);
        }
Exemple #6
0
        public static bool SetAreaData(string plusCode, string key, string value, double?expiration = null)
        {
            var db = new PraxisContext();

            if (db.PlayerData.Any(p => p.DeviceID == key || p.DeviceID == value))
            {
                return(false);
            }

            var row = db.AreaGameData.FirstOrDefault(p => p.PlusCode == plusCode && p.DataKey == key);

            if (row == null)
            {
                row              = new DbTables.AreaGameData();
                row.DataKey      = key;
                row.PlusCode     = plusCode;
                row.GeoAreaIndex = Converters.GeoAreaToPolygon(OpenLocationCode.DecodeValid(plusCode.ToUpper()));
                db.AreaGameData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }
            row.IvData    = null;
            row.DataValue = value.ToByteArrayUTF8();
            return(db.SaveChanges() == 1);
        }
Exemple #7
0
        /// <summary>
        /// Saves a key/value pair to a given player's DeviceID. Will reject a pair containing a PlusCode or map element Id.
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="key">The key to save to the database. Keys are unique, and you cannot have multiples of the same key.</param>
        /// <param name="value">The value to save with the key.</param>
        /// <param name="expiration">If not null, expire this data in this many seconds from now.</param>
        /// <returns>true if data was saved, false if data was not.</returns>
        public static bool SetPlayerData(string playerId, string key, string value, double?expiration = null)
        {
            if (DataCheck.IsPlusCode(key) || DataCheck.IsPlusCode(value))
            {
                return(false); //Reject attaching a player to a pluscode.
            }
            var  db        = new PraxisContext();
            Guid tempCheck = new Guid();

            if ((Guid.TryParse(key, out tempCheck) && db.Places.Any(osm => osm.PrivacyId == tempCheck)) ||
                (Guid.TryParse(value, out tempCheck) && db.Places.Any(osm => osm.PrivacyId == tempCheck)))
            {
                return(false); //reject attaching a player to an area
            }
            var row = db.PlayerData.FirstOrDefault(p => p.DeviceID == playerId && p.DataKey == key);

            if (row == null)
            {
                row          = new DbTables.PlayerData();
                row.DataKey  = key;
                row.DeviceID = playerId;
                db.PlayerData.Add(row);
            }
            if (expiration.HasValue)
            {
                row.Expiration = DateTime.Now.AddSeconds(expiration.Value);
            }
            else
            {
                row.Expiration = null;
            }
            row.IvData    = null;
            row.DataValue = value.ToByteArrayUTF8();
            return(db.SaveChanges() == 1);
        }
Exemple #8
0
        /// <summary>
        /// Saves a key/value pair to the database that isn't attached to anything specific. Wil reject a pair that contains a player's device ID, PlusCode, or a map element ID. Global entries cannot be set to expire.
        /// </summary>
        /// <param name="key">The key to save to the database. Keys are unique, and you cannot have multiples of the same key.</param>
        /// <param name="value">The value to save with the key.</param>
        /// <returns>true if data was saved, false if data was not.</returns>
        public static bool SetGlobalData(string key, string value)
        {
            bool trackingPlayer   = false;
            bool trackingLocation = false;

            var db = new PraxisContext();

            if (db.PlayerData.Any(p => p.DeviceID == key || p.DeviceID == value))
            {
                trackingPlayer = true;
            }

            if (DataCheck.IsPlusCode(key) || DataCheck.IsPlusCode(value))
            {
                trackingLocation = true;
            }

            Guid tempCheck = new Guid();

            if ((Guid.TryParse(key, out tempCheck) && db.Places.Any(osm => osm.PrivacyId == tempCheck)) ||
                (Guid.TryParse(value, out tempCheck) && db.Places.Any(osm => osm.PrivacyId == tempCheck)))
            {
                trackingLocation = true;
            }

            if (trackingLocation && trackingPlayer) //Do not allow players and locations to be attached on the global level as a workaround to being blocked on the individual levels.
            {
                return(false);
            }

            var row = db.GlobalDataEntries.FirstOrDefault(p => p.DataKey == key);

            if (row == null)
            {
                row         = new DbTables.GlobalDataEntries();
                row.DataKey = key;
                db.GlobalDataEntries.Add(row);
            }
            row.DataValue = value.ToByteArrayUTF8();
            return(db.SaveChanges() == 1);
        }
Exemple #9
0
        /// <summary>
        /// Creates all gameplay tiles for a given area and saves them to the database (or files, if that option is set)
        /// </summary>
        /// <param name="areaToDraw">the GeoArea of the area to create tiles for.</param>
        /// <param name="saveToFiles">If true, writes to files in the output folder. If false, saves to DB.</param>
        public static void PregenMapTilesForArea(GeoArea areaToDraw, bool saveToFiles = false)
        {
            //There is a very similar function for this in Standalone.cs, but this one writes back to the main DB.
            var intersectCheck = Converters.GeoAreaToPolygon(areaToDraw);
            //start drawing maptiles and sorting out data.
            var swCorner = new OpenLocationCode(intersectCheck.EnvelopeInternal.MinY, intersectCheck.EnvelopeInternal.MinX);
            var neCorner = new OpenLocationCode(intersectCheck.EnvelopeInternal.MaxY, intersectCheck.EnvelopeInternal.MaxX);

            //declare how many map tiles will be drawn
            var xTiles     = areaToDraw.LongitudeWidth / resolutionCell8;
            var yTiles     = areaToDraw.LatitudeHeight / resolutionCell8;
            var totalTiles = Math.Truncate(xTiles * yTiles);

            Log.WriteLog("Starting processing maptiles for " + totalTiles + " Cell8 areas.");
            long mapTileCounter = 0;

            System.Diagnostics.Stopwatch progressTimer = new System.Diagnostics.Stopwatch();
            progressTimer.Start();

            //now, for every Cell8 involved, draw and name it.
            var yCoords = new List <double>((int)(intersectCheck.EnvelopeInternal.Height / resolutionCell8) + 1);
            var yVal    = swCorner.Decode().SouthLatitude;

            while (yVal <= neCorner.Decode().NorthLatitude)
            {
                yCoords.Add(yVal);
                yVal += resolutionCell8;
            }

            var xCoords = new List <double>((int)(intersectCheck.EnvelopeInternal.Width / resolutionCell8) + 1);
            var xVal    = swCorner.Decode().WestLongitude;

            while (xVal <= neCorner.Decode().EastLongitude)
            {
                xCoords.Add(xVal);
                xVal += resolutionCell8;
            }

            var allPlaces = GetPlaces(areaToDraw);

            object   listLock   = new object();
            DateTime expiration = DateTime.Now.AddYears(10);

            foreach (var y in yCoords)
            {
                System.Diagnostics.Stopwatch thisRowSW = new System.Diagnostics.Stopwatch();
                thisRowSW.Start();
                var db = new PraxisContext();
                db.ChangeTracker.AutoDetectChangesEnabled = false;
                //Make a collision box for just this row of Cell8s, and send the loop below just the list of things that might be relevant.
                //Add a Cell8 buffer space so all elements are loaded and drawn without needing to loop through the entire area.
                GeoArea thisRow     = new GeoArea(y - ConstantValues.resolutionCell8, xCoords.First() - ConstantValues.resolutionCell8, y + ConstantValues.resolutionCell8 + ConstantValues.resolutionCell8, xCoords.Last() + resolutionCell8);
                var     rowList     = GetPlaces(thisRow, allPlaces);
                var     tilesToSave = new List <MapTile>(xCoords.Count());

                Parallel.ForEach(xCoords, x =>
                {
                    //make map tile.
                    var plusCode     = new OpenLocationCode(y, x, 10);
                    var plusCode8    = plusCode.CodeDigits.Substring(0, 8);
                    var plusCodeArea = OpenLocationCode.DecodeValid(plusCode8);
                    var paddedArea   = GeometrySupport.MakeBufferedGeoArea(plusCodeArea);

                    var acheck   = Converters.GeoAreaToPreparedPolygon(paddedArea);                   //Fastest search option is one preparedPolygon against a list of normal geometry.
                    var areaList = rowList.Where(a => acheck.Intersects(a.ElementGeometry)).ToList(); //Get the list of areas in this maptile.

                    int imgX = 0, imgY = 0;
                    GetPlusCodeImagePixelSize(plusCode8, out imgX, out imgY);
                    var info = new ImageStats(plusCodeArea, imgX, imgY);
                    //new setup.
                    var areaPaintOps = GetPaintOpsForStoredElements(areaList, "mapTiles", info);
                    var tile         = DrawPlusCode(plusCode8, areaPaintOps, "mapTiles");

                    if (saveToFiles)
                    {
                        File.WriteAllBytes("GameTiles\\" + plusCode8 + ".png", tile);
                    }
                    else
                    {
                        var thisTile = new MapTile()
                        {
                            TileData = tile, PlusCode = plusCode8, ExpireOn = expiration, AreaCovered = acheck.Geometry, StyleSet = "mapTiles"
                        };
                        lock (listLock)
                            tilesToSave.Add(thisTile);
                    }
                });
                mapTileCounter += xCoords.Count();
                if (!saveToFiles)
                {
                    db.MapTiles.AddRange(tilesToSave);
                    db.SaveChanges();
                }
                Log.WriteLog(mapTileCounter + " tiles processed, " + Math.Round((mapTileCounter / totalTiles) * 100, 2) + "% complete, " + Math.Round(xCoords.Count() / thisRowSW.Elapsed.TotalSeconds, 2) + " tiles per second.");
            }//);
            progressTimer.Stop();
            Log.WriteLog("Area map tiles drawn in " + progressTimer.Elapsed.ToString() + ", averaged " + Math.Round(mapTileCounter / progressTimer.Elapsed.TotalSeconds, 2) + " tiles per second.");
        }
Exemple #10
0
        /// <summary>
        /// Auto-generate some places to be used as gameplay elements in otherwise sparse areas. Given an 8 digit PlusCode, creates and warps some standard shapes in the Cell.
        /// </summary>
        /// <param name="plusCode">The area to generate shape(s) in</param>
        /// <param name="autoSave">If true, saves the areas to the database immediately.</param>
        /// <returns>The list of places created for the given area.</returns>
        public static List <DbTables.Place> CreateInterestingPlaces(string plusCode, bool autoSave = true)
        {
            //expected to receive a Cell8
            // populate it with some interesting regions for players.
            Random   r          = new Random();
            CodeArea cell8      = OpenLocationCode.DecodeValid(plusCode); //Reminder: area is .0025 degrees on a Cell8
            int      shapeCount = 1;                                      // 2; //number of shapes to apply to the Cell8
            double   shapeWarp  = .3;                                     //percentage a shape is allowed to have each vertexs drift by.
            List <DbTables.Place> areasToAdd = new List <DbTables.Place>();

            for (int i = 0; i < shapeCount; i++)
            {
                //Pick a shape
                var masterShape     = possibleShapes.OrderBy(s => r.Next()).First();
                var shapeToAdd      = masterShape.Select(s => new Coordinate(s)).ToList();
                var scaleFactor     = r.Next(10, 36) * .01; //Math.Clamp(r.Next, .1, .35); //Ensure that we get a value that isn't terribly useless. 2 shapes can still cover 70%+ of an empty area this way.
                var positionFactorX = r.NextDouble() * resolutionCell8;
                var positionFactorY = r.NextDouble() * resolutionCell8;
                foreach (Coordinate c in shapeToAdd)
                {
                    //scale it to our resolution
                    c.X *= resolutionCell8;
                    c.Y *= resolutionCell8;

                    //multiply this by some factor smaller than 1, so it doesn't take up the entire Cell
                    //If we use NextDouble() here, it scales each coordinate randomly, which would look very unpredictable. Use the results of one call twice to scale proportionally.
                    //but ponder how good/bad it looks for various shapes if each coordinate is scaled differently.
                    c.X *= scaleFactor;
                    c.Y *= scaleFactor;

                    //Rotate the coordinate set some random number of degrees?
                    //TODO: how to rotate these?

                    //Place the shape somewhere randomly by adding the same X/Y value less than the resolution to each point
                    c.X += positionFactorX;
                    c.Y += positionFactorY;

                    //Fuzz each vertex by adding some random distance on each axis less than 30% of the cell's size in either direction.
                    //10% makes the shapes much more recognizable, but not as interesting. Will continue looking into parameters here to help adjust that.
                    c.X += (r.NextDouble() * resolutionCell8 * shapeWarp) * (r.Next() % 2 == 0 ? 1 : -1);
                    c.Y += (r.NextDouble() * resolutionCell8 * shapeWarp) * (r.Next() % 2 == 0 ? 1 : -1);

                    //Let us know if this shape overlaps a neighboring cell. We probably want to make sure we re-draw map tiles if it does.
                    if (c.X > .0025 || c.Y > .0025)
                    {
                        Log.WriteLog("Coordinate for shape " + i + " in Cell8 " + plusCode + " will be out of bounds: " + c.X + " " + c.Y, Log.VerbosityLevels.High);
                    }

                    //And now add the minimum values for the given Cell8 to finish up coordinates.
                    c.X += cell8.Min.Longitude;
                    c.Y += cell8.Min.Latitude;
                }

                //ShapeToAdd now has a randomized layout, convert it to a polygon.
                shapeToAdd.Add(shapeToAdd.First()); //make it a closed shape
                var polygon = factory.CreatePolygon(shapeToAdd.ToArray());
                polygon = CCWCheck(polygon);        //Sometimes squares still aren't CCW? or this gets un-done somewhere later?
                if (!polygon.IsValid || !polygon.Shell.IsCCW)
                {
                    Log.WriteLog("Invalid geometry generated, retrying", Log.VerbosityLevels.High);
                    i--;
                    continue;
                }

                if (!polygon.CoveredBy(Converters.GeoAreaToPolygon(cell8)))
                {
                    //This should only ever require checking the map tile north/east of the current one, even though the vertex fuzzing can potentially move things negative slightly.
                    Log.WriteLog("This polygon is outside of the Cell8 by " + (cell8.Max.Latitude - shapeToAdd.Max(s => s.Y)) + "/" + (cell8.Max.Longitude - shapeToAdd.Max(s => s.X)), Log.VerbosityLevels.High);
                }
                if (polygon != null)
                {
                    DbTables.Place gmd = new DbTables.Place();
                    gmd.ElementGeometry = polygon;
                    gmd.GameElementName = "generated";
                    gmd.Tags.Add(new PlaceTags()
                    {
                        Key = "praxisGenerated", Value = "true"
                    });
                    areasToAdd.Add(gmd); //this is the line that makes some objects occasionally not be CCW that were CCW before. Maybe its the cast to the generic Geometry item?
                }
                else
                {
                    //Inform me that I did something wrong.
                    Log.WriteLog("failed to convert a randomized shape to a polygon.", Log.VerbosityLevels.Errors);
                    continue;
                }
            }

            //Making this function self-contained
            if (autoSave)
            {
                var db = new PraxisContext();
                foreach (var area in areasToAdd)
                {
                    area.ElementGeometry = CCWCheck((Polygon)area.ElementGeometry); //fixes errors that reappeared above
                }
                db.Places.AddRange(areasToAdd);
                db.SaveChanges();
            }

            return(areasToAdd);
        }