/// <summary> /// Erzeugt eine neue Instanz einer ChunkColumn. /// </summary> /// <param name="chunks">Die Chunks für die Säule</param> /// <param name="planet">Der Index des Planeten</param> /// <param name="columnIndex">Die Position der Säule</param> public ChunkColumn(IChunk[] chunks, int planet, Index2 columnIndex) : this() { Planet = planet; Chunks = chunks; Index = columnIndex; }
public Index2 PointToMapPosition(Point pt) { if (Map == null) return new Index2(); Index2 ind = new Index2((int)(pt.X / scale + offsetX), (int)(pt.Y / scale + offsetY)); ind.NormalizeXY(new Index2(Map.Planet.Size.X * Chunk.CHUNKSIZE_X, Map.Planet.Size.Y * Chunk.CHUNKSIZE_Y)); return ind; }
/// <summary> /// Gibt einen abonnierten Chunk wieder frei. /// </summary> /// <param name="planet">Die Id des Planeten</param> /// <param name="position">Die Position des freizugebenden Chunks</param> public void Release(int planet, Index2 position) { CacheItem cacheItem = null; lock (lockObject) { if (!cache.TryGetValue(new Index3(position, planet), out cacheItem)) { throw new NotSupportedException("Kein Chunk für Position in Cache"); } cacheItem.References--; } }
/// <summary> /// Setzt den Zentrums-Chunk für diesen lokalen Cache. /// </summary> /// <param name="planet">Der Planet, auf dem sich der Chunk befindet</param> /// <param name="index">Die Koordinaten an der sich der Chunk befindet</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> public void SetCenter(IPlanet planet, Index2 index, Action <bool> successCallback = null) { if (_loadingTask != null && !_loadingTask.IsCompleted) { _cancellationToken.Cancel(); _cancellationToken = new CancellationTokenSource(); _loadingTask = _loadingTask.ContinueWith(_ => InternalSetCenter(_cancellationToken.Token, planet, index, successCallback)); } else { _cancellationToken = new CancellationTokenSource(); _loadingTask = Task.Factory.StartNew(() => InternalSetCenter(_cancellationToken.Token, planet, index, successCallback)); } }
/// <summary> /// Liefert den Chunk an der angegebenen Chunk-Koordinate zurück. /// </summary> /// <param name="x">X Koordinate</param> /// <param name="y">Y Koordinate</param> /// <param name="z">Z Koordinate</param> /// <returns>Instanz des Chunks</returns> public IChunk GetChunk(int x, int y, int z) { if (Planet == null || z < 0 || z >= Planet.Size.Z) return null; x = Index2.NormalizeAxis(x, Planet.Size.X); y = Index2.NormalizeAxis(y, Planet.Size.Y); IChunkColumn chunkColumn = chunkColumns[FlatIndex(x, y)]; if (chunkColumn != null && chunkColumn.Index.X == x && chunkColumn.Index.Y == y) return chunkColumn.Chunks[z]; return null; }
/// <summary> /// Setzt den Zentrums-Chunk für diesen lokalen Cache. /// </summary> /// <param name="planet">Der Planet, auf dem sich der Chunk befindet</param> /// <param name="index">Die Koordinaten an der sich der Chunk befindet</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> public bool SetCenter(Index2 index, Action<bool> successCallback = null) { using (taskSemaphore.Wait()) { var callerName = new StackFrame(1).GetMethod().Name; logger.Debug($"Set Center from {callerName}"); CenterPosition = index; if (_loadingTask != null && !_loadingTask.IsCompleted) { logger.Debug("Continue with task on index " + index); _loadingTask = _loadingTask.ContinueWith(_ => InternalSetCenter(_cancellationToken.Token, index, successCallback)); } else { logger.Debug("New task on index " + index); _cancellationToken?.Cancel(); _cancellationToken?.Dispose(); _cancellationToken = new CancellationTokenSource(); _loadingTask = Task.Run(() => InternalSetCenter(_cancellationToken.Token, index, successCallback)); } } return true; }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten Z-Achse. /// </summary> /// <param name="z">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceZ(int z, int size) { return(Index2.ShortestDistanceOnAxis(Z, z, size)); }
/// <summary> /// Normalisiert die Z-Achse auf die angegebene Größe. /// </summary> /// <param name="size">Maximalwert für Z</param> public void NormalizeZ(int size) { Z = Index2.NormalizeAxis(Z, size); }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten X-Achse. /// </summary> /// <param name="x">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceX(int x, int size) { return(Index2.ShortestDistanceOnAxis(X, x, size)); }
/// <summary> /// Deserialisiert die Chunksäule aus dem angegebenen Stream. /// </summary> /// <param name="stream">Quellstream</param> /// <param name="definitionManager">Der verwendete DefinitionManager</param> /// <param name="columnIndex">Die Position der Säule</param> /// <param name="planetId">Der Index des Planeten</param> public void Deserialize(BinaryReader reader, IDefinitionManager definitionManager) { bool longIndex = reader.ReadByte() > 0; // Phase 1 (Column Meta: Heightmap, populated, chunkcount) Chunks = new Chunk[reader.ReadByte()]; // Chunk Count Populated = reader.ReadBoolean(); // Populated Index = new Index2(reader.ReadInt32(), reader.ReadInt32()); Planet = reader.ReadInt32(); for (int y = 0; y < Chunk.CHUNKSIZE_Y; y++) // Heightmap { for (int x = 0; x < Chunk.CHUNKSIZE_X; x++) { Heights[x, y] = reader.ReadUInt16(); } } int[] counter = new int[Chunks.Length]; for (int i = 0; i < Chunks.Length; i++) // ChangeCounter { counter[i] = reader.ReadInt32(); } // Phase 2 (Block Definitionen) List <IDefinition> types = new List <IDefinition>(); Dictionary <ushort, ushort> map = new Dictionary <ushort, ushort>(); int typecount = longIndex ? reader.ReadUInt16() : reader.ReadByte(); for (int i = 0; i < typecount; i++) { string typeName = reader.ReadString(); IDefinition[] definitions = definitionManager.GetDefinitions().ToArray(); var blockDefinition = definitions.FirstOrDefault(d => d.GetType().FullName == typeName); types.Add(blockDefinition); map.Add((ushort)types.Count, (ushort)(Array.IndexOf(definitions, blockDefinition) + 1)); } // Phase 3 (Chunk Infos) for (int c = 0; c < Chunks.Length; c++) { IChunk chunk = Chunks[c] = new Chunk(new Index3(Index, c), Planet); chunk.Changed += OnChunkChanged; chunk.SetColumn(this); for (int i = 0; i < chunk.Blocks.Length; i++) { ushort typeIndex = longIndex ? reader.ReadUInt16() : reader.ReadByte(); chunk.MetaData[i] = 0; if (typeIndex > 0) { chunk.Blocks[i] = map[typeIndex]; var definition = (IBlockDefinition)definitionManager.GetDefinitionByIndex(map[typeIndex]); if (definition.HasMetaData) { chunk.MetaData[i] = reader.ReadInt32(); } } } chunk.ChangeCounter = counter[c]; } //Entities lesen var count = reader.ReadInt32(); for (int i = 0; i < count; i++) { var name = reader.ReadString(); var length = reader.ReadInt32(); byte[] buffer = new byte[length]; reader.Read(buffer, 0, length); try { var type = Type.GetType(name); if (type == null) { continue; } Entity entity = (Entity)Activator.CreateInstance(type); using (MemoryStream memorystream = new MemoryStream(buffer)) { using (BinaryReader componentbinarystream = new BinaryReader(memorystream)) { entity.Deserialize(componentbinarystream, definitionManager); } } Entities.Add(entity); } catch (Exception) { } } }
/// <summary> /// Deserialisiert die Chunksäule aus dem angegebenen Stream. /// </summary> /// <param name="stream">Quellstream</param> /// <param name="definitionManager">Der verwendete DefinitionManager</param> /// <param name="columnIndex">Die Position der Säule</param> /// <param name="planetId">Der Index des Planeten</param> public void Deserialize(Stream stream, IDefinitionManager definitionManager, int planetId, Index2 columnIndex) { using (BinaryReader br = new BinaryReader(stream)) { bool longIndex = br.ReadByte() > 0; // Phase 1 (Column Meta: Heightmap, populated, chunkcount) Chunks = new Chunk[br.ReadByte()]; // Chunk Count Planet = planetId; Index = columnIndex; Populated = br.ReadBoolean(); // Populated for (int y = 0; y < Chunk.CHUNKSIZE_Y; y++) // Heightmap for (int x = 0; x < Chunk.CHUNKSIZE_X; x++) Heights[x, y] = br.ReadUInt16(); int[] counter = new int[Chunks.Length]; for (int i = 0; i < Chunks.Length; i++) // ChangeCounter counter[i] = br.ReadInt32(); // Phase 2 (Block Definitionen) List<IBlockDefinition> types = new List<IBlockDefinition>(); Dictionary<ushort, ushort> map = new Dictionary<ushort, ushort>(); int typecount = longIndex ? br.ReadUInt16() : br.ReadByte(); for (int i = 0; i < typecount; i++) { string typeName = br.ReadString(); IBlockDefinition[] definitions = definitionManager.GetBlockDefinitions().ToArray(); var blockDefinition = definitions.FirstOrDefault(d => d.GetType().FullName == typeName); types.Add(blockDefinition); map.Add((ushort)types.Count, (ushort)(Array.IndexOf(definitions, blockDefinition) + 1)); } // Phase 3 (Chunk Infos) for (int c = 0; c < Chunks.Length; c++) { IChunk chunk = Chunks[c] = new Chunk(new Index3(columnIndex, c), planetId); for (int i = 0; i < chunk.Blocks.Length; i++) { ushort typeIndex = longIndex ? br.ReadUInt16() : br.ReadByte(); chunk.MetaData[i] = 0; if (typeIndex > 0) { chunk.Blocks[i] = map[typeIndex]; var definition = definitionManager.GetBlockDefinitionByIndex(map[typeIndex]); if (definition.HasMetaData) chunk.MetaData[i] = br.ReadInt32(); } } chunk.ChangeCounter = counter[c]; } } }
/// <summary> /// Normalisiert die Y-Achse auf die angegebene Größe. /// </summary> /// <param name="size">2D-Größe (Y-Anzeil wird genommen)</param> public void NormalizeY(Index2 size) { NormalizeY(size.Y); }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf den normalisierten Achsen. /// </summary> /// <param name="destination">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public Index3 ShortestDistanceXY(Index3 destination, Index2 size) => new Index3( ShortestDistanceX(destination.X, size.X), ShortestDistanceY(destination.Y, size.Y), destination.Z - Z);
public bool IsChunkLoaded(int planet, Index2 position) => cache.ContainsKey(new Index3(position, planet));
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten Y-Achse. /// </summary> /// <param name="y">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceY(int y, int size) => Index2.ShortestDistanceOnAxis(Y, y, size);
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten Z-Achse. /// </summary> /// <param name="z">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceZ(int z, int size) => Index2.ShortestDistanceOnAxis(Z, z, size);
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten X-Achse. /// </summary> /// <param name="x">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceX(int x, int size) => Index2.ShortestDistanceOnAxis(X, x, size);
/// <summary> /// Normalisiert die X- und Y-Achse auf die angegebene Größe. /// </summary> /// <param name="size">Maximalwert für X und Y</param> public void NormalizeXY(Index2 size) => NormalizeXY(size.X, size.Y);
private void RenderToBackBuffer() { int offsetX = (int)(this.offsetX / Chunk.CHUNKSIZE_X), offsetY = (int)(this.offsetY / Chunk.CHUNKSIZE_Y); BitmapData bmpData = backBuffer.LockBits(new Rectangle(new Point(), backBuffer.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int[] emptyData = new int[Chunk.CHUNKSIZE_X * Chunk.CHUNKSIZE_Y]; unsafe { int* ptr = (int*)bmpData.Scan0; for (int y = offsetY ; y < offsetY + sizeY; y++) { for (int x = offsetX ; x < offsetX + sizeX; x++) { if ((y - offsetY) < 0 || (x - offsetX) < 0) continue; Index2 normalized = new Index2(x, y); normalized.NormalizeXY(Map.Planet.Size); int index = normalized.Y * Map.Planet.Size.X + normalized.X; int offset = (y - offsetY) * Chunk.CHUNKSIZE_Y * backBuffer.Width + (x - offsetX) * Chunk.CHUNKSIZE_X; if (Map.Chunks[index] == null) { CopyRegion(backBuffer, ptr, offset, Chunk.CHUNKSIZE_X, Chunk.CHUNKSIZE_Y, emptyData); continue; } int[] bmp = Map.Chunks[index].GetBitmapData(); if (bmp == null) continue; CopyRegion(backBuffer, ptr, offset, Chunk.CHUNKSIZE_X, Chunk.CHUNKSIZE_Y, bmp); //Chunk.CHUNKSIZE_X, Chunk.CHUNKSIZE_Y index++; } } } backBuffer.UnlockBits(bmpData); }
/// <summary> /// Initialisierung /// </summary> /// <param name="index">2D-Basis</param> /// <param name="z">Z-Anteil</param> public Index3(Index2 index, int z) : this(index.X, index.Y, z) { }
/// <summary> /// Normalisiert den Wert von X und Y auf den angegebenen Grenzbereich. /// </summary> /// <param name="index">Der zu normalisierende Index2</param> /// <param name="size">3D Size</param> public static Index2 NormalizeXY(Index2 index,Index3 size) { index.NormalizeXY(size); return index; }
/// <summary> /// Normalisiert die X-Achse auf die angegebene Größe. /// </summary> /// <param name="size">2D-Size</param> public void NormalizeX(Index2 size) { NormalizeX(size.X); }
/// <summary> /// Normalisiert die X-, Y- und Z-Achse auf die angegebene Größe. /// </summary> /// <param name="size">2D-Size</param> /// <param name="z">Z-Anteil</param> public void NormalizeXYZ(Index2 size, int z) { NormalizeXYZ(size.X, size.Y, z); }
/// <summary> /// Normalisiert die X-Achse auf die angegebene Größe. /// </summary> /// <param name="size">2D-Größe (X-Anzeil wird genommen)</param> public void NormalizeX(Index2 size) { NormalizeX(size.X); }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf den normalisierten Achsen. /// </summary> /// <param name="destination">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public Index3 ShortestDistanceXY(Index3 destination, Index2 size) { return new Index3( ShortestDistanceX(destination.X, size.X), ShortestDistanceY(destination.Y, size.Y), destination.Z - Z); }
/// <summary> /// Liefert den Chunk, sofern geladen. /// </summary> /// <param name="planet">Die Id des Planeten</param> /// <param name="position">Die Position des zurückzugebenden Chunks</param> /// <returns>Chunk Instanz oder null, falls nicht geladen</returns> public IChunkColumn Peek(int planet, Index2 position) { CacheItem cacheItem = null; if (cache.TryGetValue(new Index3(position, planet), out cacheItem)) { return cacheItem.ChunkColumn; } return null; }
/// <summary> /// Abonniert einen Chunk. /// </summary> /// <param name="planet">Die Id des Planeten</param> /// <param name="position">Position des Chunks</param> /// <returns></returns> public IChunkColumn Subscribe(int planet, Index2 position) { CacheItem cacheItem = null; lock (lockObject) { if (!cache.TryGetValue(new Index3(position, planet), out cacheItem)) { cacheItem = new CacheItem() { References = 0, ChunkColumn = null, }; cache.Add(new Index3(position, planet), cacheItem); } cacheItem.References++; } lock (cacheItem) { if (cacheItem.ChunkColumn == null) { cacheItem.ChunkColumn = loadDelegate(planet, position); cacheItem.SavedChangeCounter = cacheItem.ChunkColumn.Chunks.Select(c => c.ChangeCounter).ToArray(); } } return cacheItem.ChunkColumn; }
/// <summary> /// Initialisierung /// </summary> /// <param name="value">Initialwerte</param> public Index2(Index2 value) : this(value.X, value.Y) { }
/// <summary> /// Deserialisiert die Chunksäule aus dem angegebenen Stream. /// </summary> /// <param name="stream">Quellstream</param> /// <param name="definitionManager">Der verwendete DefinitionManager</param> /// <param name="columnIndex">Die Position der Säule</param> /// <param name="planetId">Der Index des Planeten</param> public void Deserialize(Stream stream, IDefinitionManager definitionManager, int planetId, Index2 columnIndex) { using (BinaryReader br = new BinaryReader(stream)) { bool longIndex = br.ReadByte() > 0; // Phase 1 (Column Meta: Heightmap, populated, chunkcount) Chunks = new Chunk[br.ReadByte()]; // Chunk Count Planet = planetId; Index = columnIndex; Populated = br.ReadBoolean(); // Populated for (int y = 0; y < Chunk.CHUNKSIZE_Y; y++) // Heightmap { for (int x = 0; x < Chunk.CHUNKSIZE_X; x++) { Heights[x, y] = br.ReadUInt16(); } } int[] counter = new int[Chunks.Length]; for (int i = 0; i < Chunks.Length; i++) // ChangeCounter { counter[i] = br.ReadInt32(); } // Phase 2 (Block Definitionen) List <IBlockDefinition> types = new List <IBlockDefinition>(); Dictionary <ushort, ushort> map = new Dictionary <ushort, ushort>(); int typecount = longIndex ? br.ReadUInt16() : br.ReadByte(); for (int i = 0; i < typecount; i++) { string typeName = br.ReadString(); IBlockDefinition[] definitions = definitionManager.GetBlockDefinitions().ToArray(); var blockDefinition = definitions.FirstOrDefault(d => d.GetType().FullName == typeName); types.Add(blockDefinition); map.Add((ushort)types.Count, (ushort)(Array.IndexOf(definitions, blockDefinition) + 1)); } // Phase 3 (Chunk Infos) for (int c = 0; c < Chunks.Length; c++) { IChunk chunk = Chunks[c] = new Chunk(new Index3(columnIndex, c), planetId); for (int i = 0; i < chunk.Blocks.Length; i++) { ushort typeIndex = longIndex ? br.ReadUInt16() : br.ReadByte(); chunk.MetaData[i] = 0; if (typeIndex > 0) { chunk.Blocks[i] = map[typeIndex]; var definition = definitionManager.GetBlockDefinitionByIndex(map[typeIndex]); if (definition.HasMetaData) { chunk.MetaData[i] = br.ReadInt32(); } } } chunk.ChangeCounter = counter[c]; } } }
/// <summary> /// Normalisiert die Y-Achse auf die angegebene Größe. /// </summary> /// <param name="size">2D-Size</param> public void NormalizeY(Index2 size) { NormalizeY(size.Y); }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf einer normalisierten Y-Achse. /// </summary> /// <param name="y">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public int ShortestDistanceY(int y, int size) { return(Index2.ShortestDistanceOnAxis(Y, y, size)); }
void LoadViewRange(int offsetX, int offsetY, int sizeX, int sizeY) { for (int y = offsetY; y < offsetY + sizeY; y++) { for (int x = offsetX; x < offsetX + sizeX; x++) { Index2 normalized = new Index2(x, y); normalized.NormalizeXY(Map.Planet.Size); int index = normalized.Y * Map.Planet.Size.X + normalized.X; if (Map.Chunks[index] != null) continue; IChunk[] tempChunks = Map.Generator.GenerateChunk (BlockDefinitionManager.GetBlockDefinitions(), Map.Planet, normalized); Map.Chunks[index] = new Chunk2D(tempChunks); if (!IsHandleCreated) return; } } }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf den normalisierten Achsen. /// </summary> /// <param name="destination">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public Index2 ShortestDistanceXY(Index2 destination, Index2 size) { return(new Index2( ShortestDistanceX(destination.X, size.X), ShortestDistanceY(destination.Y, size.Y))); }
/// <summary> /// Interne Methode, in der der zentrale Chunk gesetzt wird. Die Chunks um den Zentrumschunk werden auch nachgeladen falls nötig /// </summary> /// <param name="token">Token, um zu prüfen, ob die aktualisierung abgeborchen werden soll</param> /// <param name="planet">Der Planet, auf dem die Chunks aktualisiert werden sollen</param> /// <param name="index">Der ins Zentrum zu setzende Chunk</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> private void InternalSetCenter(CancellationToken token, IPlanet planet, Index2 index, Action <bool> successCallback) { if (planet == null) { successCallback?.Invoke(true); return; } List <Index2> requiredChunkColumns = new List <Index2>(); for (int x = -range; x <= range; x++) { for (int y = -range; y <= range; y++) { Index2 local = new Index2(index.X + x, index.Y + y); local.NormalizeXY(planet.Size); requiredChunkColumns.Add(local); } } // Erste Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } foreach (var chunkColumnIndex in requiredChunkColumns .OrderBy(c => index.ShortestDistanceXY(c, new Index2(planet.Size)) .LengthSquared())) { int localX = chunkColumnIndex.X & mask; int localY = chunkColumnIndex.Y & mask; int flatIndex = FlatIndex(localX, localY); IChunkColumn chunkColumn = chunkColumns[flatIndex]; // Alten Chunk entfernen, falls notwendig if (chunkColumn != null && chunkColumn.Index != chunkColumnIndex) { globalCache.Release(planet.Id, chunkColumn.Index, IsPassive); chunkColumns[flatIndex] = null; chunkColumn = null; } // Zweite Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } // Neuen Chunk laden if (chunkColumn == null) { chunkColumn = globalCache.Subscribe(planet.Id, new Index2(chunkColumnIndex), IsPassive); chunkColumns[flatIndex] = chunkColumn; if (chunkColumn == null) { successCallback?.Invoke(false); return; } } // Dritte Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } } successCallback?.Invoke(true); }
/// <summary> /// Normalisiert die X-Achse auf die angegebene Größe. /// </summary> /// <param name="size">Maximalwert für X</param> public void NormalizeX(int size) { X = Index2.NormalizeAxis(X, size); }
/// <summary> /// Interne Methode, in der der zentrale Chunk gesetzt wird. Die Chunks um den Zentrumschunk werden auch nachgeladen falls nötig /// </summary> /// <param name="token">Token, um zu prüfen, ob die aktualisierung abgeborchen werden soll</param> /// <param name="planet">Der Planet, auf dem die Chunks aktualisiert werden sollen</param> /// <param name="index">Der ins Zentrum zu setzende Chunk</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> private void InternalSetCenter(CancellationToken token, IPlanet planet, Index2 index, Action<bool> successCallback) { // Planet resetten falls notwendig if (this.planet != planet) InitializePlanet(planet); if (planet == null) { if (successCallback != null) successCallback(true); return; } List<Index2> requiredChunkColumns = new List<Index2>(); for (int x = -range; x <= range; x++) { for (int y = -range; y <= range; y++) { Index2 local = new Index2(index.X + x, index.Y + y); local.NormalizeXY(planet.Size); requiredChunkColumns.Add(local); } } // Erste Abbruchmöglichkeit if (token.IsCancellationRequested) { if (successCallback != null) successCallback(false); return; } foreach (var chunkColumnIndex in requiredChunkColumns.OrderBy(c => index.ShortestDistanceXY(c, new Index2(planet.Size)).LengthSquared())) { int localX = chunkColumnIndex.X & mask; int localY = chunkColumnIndex.Y & mask; int flatIndex = FlatIndex(localX, localY); IChunkColumn chunkColumn = chunkColumns[flatIndex]; // Alten Chunk entfernen, falls notwendig if (chunkColumn != null && chunkColumn.Index != chunkColumnIndex) { globalCache.Release(planet.Id, chunkColumn.Index); chunkColumns[flatIndex] = null; chunkColumn = null; } // Zweite Abbruchmöglichkeit if (token.IsCancellationRequested) { if (successCallback != null) successCallback(false); return; } // Neuen Chunk laden if (chunkColumn == null) { chunkColumn = globalCache.Subscribe(planet.Id, new Index2(chunkColumnIndex)); chunkColumns[flatIndex] = chunkColumn; } // Dritte Abbruchmöglichkeit if (token.IsCancellationRequested) { if (successCallback != null) successCallback(false); return; } } if (successCallback != null) successCallback(true); }
/// <summary> /// Normalisiert die Y-Achse auf die angegebene Größe. /// </summary> /// <param name="size">Maximalwert für Y</param> public void NormalizeY(int size) { Y = Index2.NormalizeAxis(Y, size); }
/// <summary> /// Deserialisiert die Chunksäule aus dem angegebenen Stream. /// </summary> /// <param name="stream">Quellstream</param> /// <param name="definitionManager">Der verwendete DefinitionManager</param> /// <param name="columnIndex">Die Position der Säule</param> /// <param name="planetId">Der Index des Planeten</param> public void Deserialize(BinaryReader reader) { var longIndex = reader.ReadByte() > 0; // Phase 1 (Column Meta: Heightmap, populated, chunkcount) Chunks = new Chunk[reader.ReadByte()]; // Chunk Count Populated = reader.ReadBoolean(); // Populated Index = new Index2(reader.ReadInt32(), reader.ReadInt32()); int planetId = reader.ReadInt32(); var resManager = TypeContainer.Get <IResourceManager>(); Planet = resManager.GetPlanet(planetId); for (var y = 0; y < Chunk.CHUNKSIZE_Y; y++) // Heightmap { for (var x = 0; x < Chunk.CHUNKSIZE_X; x++) { Heights[x, y] = reader.ReadUInt16(); } } // Phase 2 (Block Definitionen) var types = new List <IDefinition>(); var map = new Dictionary <ushort, ushort>(); int typecount = longIndex ? reader.ReadUInt16() : reader.ReadByte(); for (var i = 0; i < typecount; i++) { var typeName = reader.ReadString(); IDefinition[] definitions = DefinitionManager.GetDefinitions().ToArray(); IDefinition blockDefinition = definitions.FirstOrDefault(d => d.GetType().FullName == typeName); types.Add(blockDefinition); map.Add((ushort)types.Count, (ushort)(Array.IndexOf(definitions, blockDefinition) + 1)); } // Phase 3 (Chunk Infos) for (var c = 0; c < Chunks.Length; c++) { IChunk chunk = Chunks[c] = new Chunk(new Index3(Index, c), Planet); chunk.Changed += OnChunkChanged; chunk.SetColumn(this); for (var i = 0; i < chunk.Blocks.Length; i++) { var typeIndex = longIndex ? reader.ReadUInt16() : reader.ReadByte(); chunk.MetaData[i] = 0; if (typeIndex > 0) { chunk.Blocks[i] = map[typeIndex]; var definition = (IBlockDefinition)DefinitionManager.GetDefinitionByIndex(map[typeIndex]); if (definition.HasMetaData) { chunk.MetaData[i] = reader.ReadInt32(); } } } } }
/// <summary> /// Interne Methode, in der der zentrale Chunk gesetzt wird. Die Chunks um den Zentrumschunk werden auch nachgeladen falls nötig /// </summary> /// <param name="token">Token, um zu prüfen, ob die aktualisierung abgeborchen werden soll</param> /// <param name="planet">Der Planet, auf dem die Chunks aktualisiert werden sollen</param> /// <param name="index">Der ins Zentrum zu setzende Chunk</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> private void InternalSetCenter(CancellationToken token, Index2 index, Action<bool> successCallback) { if (Planet == null) { successCallback?.Invoke(true); return; } List<Index2> requiredChunkColumns = new List<Index2>(); for (int x = -range; x <= range; x++) { for (int y = -range; y <= range; y++) { Index2 local = new Index2(index.X + x, index.Y + y); local.NormalizeXY(Planet.Size); requiredChunkColumns.Add(local); } } // Erste Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } foreach (var chunkColumnIndex in requiredChunkColumns .OrderBy(c => index.ShortestDistanceXY(c, new Index2(Planet.Size)) .LengthSquared())) { int localX = chunkColumnIndex.X & mask; int localY = chunkColumnIndex.Y & mask; int flatIndex = FlatIndex(localX, localY); IChunkColumn chunkColumn = chunkColumns[flatIndex]; // Alten Chunk entfernen, falls notwendig using (semaphore.Wait()) { if (chunkColumn != null && chunkColumn.Index != chunkColumnIndex) { //logger.Debug($"Remove Chunk: {chunkColumn.Index}, new: {chunkColumnIndex}"); globalCache.Release(chunkColumn.Index); chunkColumns[flatIndex] = null; chunkColumn = null; } } // Zweite Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } using (semaphore.Wait()) { // Neuen Chunk laden if (chunkColumn == null) { chunkColumn = globalCache.Subscribe(new Index2(chunkColumnIndex)); if (chunkColumn?.Index != chunkColumnIndex) logger.Error($"Loaded Chunk Index: {chunkColumn?.Index}, wanted: {chunkColumnIndex} "); if (chunkColumns[flatIndex] != null) logger.Error($"Chunk in Array!!: {flatIndex}, on index: {chunkColumns[flatIndex].Index} "); chunkColumns[flatIndex] = chunkColumn; if (chunkColumn == null) { successCallback?.Invoke(false); return; } } } // Dritte Abbruchmöglichkeit if (token.IsCancellationRequested) { successCallback?.Invoke(false); return; } } successCallback?.Invoke(true); }
/// <summary> /// Normalisiert den Wert von X und Y auf den angegebenen Grenzbereich. /// </summary> /// <param name="index">Der zu normalisierende Index2</param> /// <param name="size">3D Size</param> public static Index2 NormalizeXY(Index2 index, Index3 size) { index.NormalizeXY(size); return(index); }
/// <summary> /// Ermittelt die kürzeste Entfernung zum Ziel auf den normalisierten Achsen. /// </summary> /// <param name="destination">Ziel</param> /// <param name="size">Normalisierungsgröße</param> /// <returns>Entfernung</returns> public Index2 ShortestDistanceXY(Index2 destination, Index2 size) { return new Index2( ShortestDistanceX(destination.X, size.X), ShortestDistanceY(destination.Y, size.Y)); }
/// <summary> /// Initialisierung /// </summary> /// <param name="value">Initialwerte</param> public Index2(Index2 value) : this(value.X, value.Y) {}
/// <summary> /// Setzt den Zentrums-Chunk für diesen lokalen Cache. /// </summary> /// <param name="planetid">ID des Planet, auf dem sich der Chunk befindet</param> /// <param name="index">Die Koordinaten an der sich der Chunk befindet</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> public bool SetCenter(int planetid, Index2 index, Action <bool> successCallback = null) => SetCenter(globalCache.GetPlanet(planetid), index, successCallback);
/// <summary> /// Setzt den Zentrums-Chunk für diesen lokalen Cache. /// </summary> /// <param name="planet">Der Planet, auf dem sich der Chunk befindet</param> /// <param name="index">Die Koordinaten an der sich der Chunk befindet</param> /// <param name="successCallback">Routine die Aufgerufen werden soll, falls das setzen erfolgreich war oder nicht</param> public void SetCenter(IPlanet planet, Index2 index, Action<bool> successCallback = null) { if (_loadingTask != null && !_loadingTask.IsCompleted) { _cancellationToken.Cancel(); _cancellationToken = new CancellationTokenSource(); _loadingTask = _loadingTask.ContinueWith(_ => InternalSetCenter(_cancellationToken.Token, planet, index, successCallback)); } else { _cancellationToken = new CancellationTokenSource(); _loadingTask = Task.Factory.StartNew(() => InternalSetCenter(_cancellationToken.Token, planet, index, successCallback)); } }