public ProgressionCounter BeginFileCreation()
        {
            m_writer = new BinaryWriter(File.Create(MapsDataFile));

            m_writer.Write("MAPS".ToCharArray());
            m_writer.Write(CurrentMapsFileVersion); // version
            m_writer.Write(0);                      // table offset
            m_writer.Write(0);                      // total length

            m_progression = new ProgressionCounter(MapsCount);
            IEnumerable <DlmMap> maps = EnumerateClientMaps(Map.GenericDecryptionKey);
            int counter = 0;

            Task.Factory.StartNew(() =>
            {
                foreach (DlmMap map in maps)
                {
                    var position = ObjectDataManager.Instance.GetOrDefault <MapPosition>(map.Id);
                    m_offsetsTable.TryAdd(map.Id, (int)m_writer.BaseStream.Position);
                    Serializer.SerializeWithLengthPrefix(m_writer.BaseStream, new MapData(map, position), PrefixStyle.Fixed32);

                    m_progression.UpdateValue(counter++);
                }
            }).ContinueWith((task) => EndFileCreation());

            return(m_progression);
        }
        private void GenerateSubMaps(ProgressionCounter progression)
        {
            double total = MapsManager.Instance.MapsCount;

            progression.UpdateValue(0, "Loading all maps ...");
            m_mapsPosition = MapsPositionManager.Instance.EnumerateAllMaps().ToDictionary(x => x.MapId);
            int counter = 0;

            Parallel.ForEach(MapsManager.Instance.EnumerateMaps(), map =>
            {
                var builder = new SubMapBuilder();
                AdjacentSubMap[] submaps = builder.GenerateBinders(map);

                m_submaps.TryAdd(map.Id, submaps);

                // update the counter (in percent)
                Interlocked.Increment(ref counter);
                if (counter % 30 == 0)
                {
                    lock (progression)
                    {
                        if (counter % 30 == 0)
                        {
                            progression.UpdateValue(total == 0 ? 100d : (counter / total) * 100d);
                        }
                    }
                }
            });

            progression.UpdateValue(0, "Binding submaps together ...");
            counter = 0;
            Parallel.ForEach(m_submaps, cacheEntry =>
            {
                var neighbours = new[]
                {
                    TryGetMapNeighbour(cacheEntry.Key, MapNeighbour.Right),
                    TryGetMapNeighbour(cacheEntry.Key, MapNeighbour.Top),
                    TryGetMapNeighbour(cacheEntry.Key, MapNeighbour.Left),
                    TryGetMapNeighbour(cacheEntry.Key, MapNeighbour.Bottom),
                };

                foreach (AdjacentSubMap submap in cacheEntry.Value)
                {
                    for (MapNeighbour neighbour = MapNeighbour.Right; neighbour <= MapNeighbour.Bottom; neighbour++)
                    {
                        int i = (int)neighbour - 1;

                        if (neighbours[i] == null)
                        {
                            continue;
                        }

                        MapNeighbour opposite = GetOppositeDirection(neighbour);
                        AdjacentSubMap[] submaps;
                        int mapChangeData         = Map.MapChangeDatas[neighbour];
                        int oppositeMapChangeData = Map.MapChangeDatas[neighbour];
                        int cellChangement        = Map.MapCellChangement[neighbour];

                        if (neighbours[i] != null && m_submaps.TryGetValue(neighbours[i].Value, out submaps))
                        {
                            lock (submaps)
                                foreach (AdjacentSubMap neighbourSubmap in submaps)
                                {
                                    // neighbor already set
                                    lock (submap.SubMap.Neighbours)
                                        if (submap.SubMap.Neighbours.Any(x => x.GlobalId == neighbourSubmap.SubMap.GlobalId))
                                        {
                                            continue;
                                        }

                                    // if any cell of the submaps is a transition to another submap
                                    AdjacentSubMap submap1 = neighbourSubmap;
                                    short[] links          = submap.ChangeCells.Where(x => (x.MapChangeData & mapChangeData) != 0 &&
                                                                                      submap1.ChangeCells.Any(y => y.Id == x.Id + cellChangement)).Select(x => x.Id).ToArray();
                                    if (links.Length > 0)
                                    {
                                        // set in the two ways
                                        lock (submap.SubMap.Neighbours)
                                            lock (neighbourSubmap.SubMap.Neighbours)
                                            {
                                                submap.SubMap.Neighbours.Add(new SubMapNeighbour(neighbourSubmap.SubMap.GlobalId, new MovementTransition(neighbour, links)));
                                                neighbourSubmap.SubMap.Neighbours.Add(new SubMapNeighbour(submap.SubMap.GlobalId,
                                                                                                          new MovementTransition(opposite, links.Select(x => (short)(x + cellChangement)).ToArray())));
                                            }
                                    }
                                }
                        }
                    }
                }

                // update the counter (in percent)
                Interlocked.Increment(ref counter);
                if (counter % 30 == 0)
                {
                    lock (progression)
                    {
                        if (counter % 30 == 0)
                        {
                            progression.UpdateValue(counter / (double)m_submaps.Count * 100d);
                        }
                    }
                }
            });


            using (IRedisClient client = m_clientManager.GetClient())
            {
                progression.UpdateValue(0, "Storing informations on Redis server...");

                IRedisTypedClient <SubMapBinder> typedClient1 = client.As <SubMapBinder>();
                typedClient1.SetRangeInHash(typedClient1.GetHash <long>(REDIS_KEY), m_submaps.Values.SelectMany(x => x).ToDictionary(x => x.SubMap.GlobalId, x => x.SubMap));
                progression.UpdateValue(50);

                IRedisTypedClient <long[]> typedClient2 = client.As <long[]>();
                typedClient2.SetRangeInHash(typedClient2.GetHash <int>(REDIS_MAPS), m_submaps.ToDictionary(x => x.Key, x => x.Value.Select(y => y.SubMap.GlobalId).ToArray()));
                progression.UpdateValue(100);

                client.Set(REDIS_VERSION, VERSION);
            }

            m_submaps.Clear();
            m_mapsPosition.Clear();

            progression.NotifyEnded();
        }
        public ProgressionCounter BeginGeneration()
        {
            var progression = new ProgressionCounter();
            var maps        = MapsManager.Instance.EnumerateMaps();

            Task.Factory.StartNew(() =>
            {
                // step 1 : load areas stuff
                progression.UpdateValue(0, "(1/4) Getting areas ...");
                m_subAreas = ObjectDataManager.Instance.EnumerateObjects <SubArea>().ToDictionary(x => x.id);
                progression.UpdateValue(33);
                m_areas = ObjectDataManager.Instance.EnumerateObjects <Area>().ToDictionary(x => x.id);
                progression.UpdateValue(66);
                m_superAreas = ObjectDataManager.Instance.EnumerateObjects <SuperArea>().ToDictionary(x => x.id);
                progression.UpdateValue(100);

                // step 2 : bind to each map his parents areas
                progression.UpdateValue(0, "(2/4) Getting maps ...");
                var mapsPosition  = new List <MapPositionData>();
                int counter       = 0;
                progression.Total = MapsManager.Instance.MapsCount;
                foreach (var map in maps)
                {
                    var pos       = new Point(map.X, map.Y);
                    var subArea   = m_subAreas.ContainsKey(map.SubAreaId) ? m_subAreas[map.SubAreaId] : null;
                    var area      = subArea != null && m_areas.ContainsKey(subArea.areaId) ? m_areas[subArea.areaId] : null;
                    var superArea = area != null && m_subAreas.ContainsKey(area.superAreaId) ? m_superAreas[area.superAreaId] : null;

                    var mapWithPrority = new MapWithPriority(map);

                    if (subArea != null)
                    {
                        m_subAreaMaps.AddRegion(subArea.id, map.Id);
                        m_subAreaMaps.AddMap(subArea.id, pos, mapWithPrority);
                    }

                    if (area != null)
                    {
                        if (!m_areaChildrens.ContainsRegion(area.id, subArea.id))
                        {
                            m_areaChildrens.AddRegion(area.id, subArea.id);
                        }
                        m_areaChildrens.AddMap(area.id, pos, mapWithPrority);
                    }

                    if (superArea != null)
                    {
                        if (!m_superAreaChildrens.ContainsRegion(superArea.id, area.id))
                        {
                            m_superAreaChildrens.AddRegion(superArea.id, area.id);
                        }
                        m_superAreaChildrens.AddMap(superArea.id, pos, mapWithPrority);
                    }

                    int?worldmapId = superArea != null ? (int?)superArea.worldmapId : null;
                    if (superArea != null)
                    {
                        if (!m_worldMapsChildrens.ContainsRegion(worldmapId.Value, superArea.id))
                        {
                            m_worldMapsChildrens.AddRegion(worldmapId.Value, superArea.id);
                        }
                        m_worldMapsChildrens.AddMap(worldmapId.Value, pos, mapWithPrority);
                    }

                    mapsPosition.Add(new MapPositionData
                    {
                        MapId       = map.Id,
                        SubAreaId   = subArea != null ? subArea.id : (int?)null,
                        AreaId      = area != null ? area.id : (int?)null,
                        SuperAreaId = superArea != null ? superArea.id : (int?)null,
                        WorldMapId  = worldmapId,
                        X           = map.X,
                        Y           = map.Y
                    });
                    progression.UpdateValue(counter++);
                }

                progression.UpdateValue(0, "(3/4) Finding neighbours ...");
                progression.Total = mapsPosition.Count;
                // step 3 : found for each map his neighbours
                foreach (var map in mapsPosition)
                {
                    var enumerator = FindMapNeighbours(map).GetEnumerator();
                    enumerator.MoveNext();
                    map.RightNeighbourId = enumerator.Current;
                    enumerator.MoveNext();
                    map.TopNeighbourId = enumerator.Current;
                    enumerator.MoveNext();
                    map.LeftNeighbourId = enumerator.Current;
                    enumerator.MoveNext();
                    map.BottomNeighbourId = enumerator.Current;

                    Debug.Assert(!enumerator.MoveNext());
                    progression.Value++;
                }

                progression.UpdateValue(0, "(4/4) Saving ...");
                // step 4 : save all the datas and dispose the allocated lists
                using (var client = GetClient())
                {
                    var typed = client.As <MapPositionData>();
                    typed.SetRangeInHash(typed.GetHash <int>(REDIS_KEY), mapsPosition.ToDictionary(x => x.MapId));
                    client.Set(REDIS_VERSION, VERSION);
                }

                // dispose
                m_subAreaMaps.Dispose();
                m_areaChildrens.Dispose();
                m_superAreaChildrens.Dispose();
                m_worldMapsChildrens.Dispose();

                m_subAreas.Clear();
                m_areas.Clear();
                m_superAreas.Clear();

                progression.NotifyEnded();
            });
            return(progression);
        }