private void NotifyChunkToClients(ChunkEntry entry) { int notifyState = Interlocked.CompareExchange(ref entry.NotifyStatus, ChunkEntry.Notified, ChunkEntry.NotNotified); if (notifyState == ChunkEntry.Notified || entry.Requests.IsEmpty) return; Task.Factory.StartNew(() => { Parallel.For(0, entry.Requests.Count, (i) => { ClientRequest req; if (!entry.Requests.TryDequeue(out req)) return; if (entry.ChunkRequested.LightToRecalculate) entry.ChunkRequested.RecalculateSky(); req.ClientRequesting.Owner.LoadedChunks.TryUpdate(entry.ChunkRequested.Coords.ChunkPackedCoords, entry.ChunkRequested, null); entry.ChunkRequested.AddClient(req.ClientRequesting); req.ClientRequesting.SendPreChunk(entry.ChunkRequested.Coords.ChunkX, entry.ChunkRequested.Coords.ChunkZ, true, false); req.ClientRequesting.SendChunk(entry.ChunkRequested, false); }); }); }
private Chunk LoadChunk(UniversalCoords coords, bool create, Client client = null, bool sync = true) { ChunkEntry newEntry = new ChunkEntry(); ChunkEntry entry; ChunkEntry removedEntry; entry = PendingChunks.GetOrAdd(coords.ChunkPackedCoords, newEntry); int state = Interlocked.CompareExchange(ref entry.State, ChunkEntry.InProgress, ChunkEntry.NotInitialized); Chunk chunk; if (state == ChunkEntry.NotInitialized) { int threads; Interlocked.Increment(ref entry.ThreadsWaiting); // The entry could have been just readded but the chunk is already initialized if ((chunk = Chunks[coords]) != null) { threads = Interlocked.Decrement(ref entry.ThreadsWaiting); if (threads == 0) PendingChunks.TryRemove(coords.ChunkPackedCoords, out removedEntry); entry.ChunkRequested = chunk; entry.State = ChunkEntry.Initialized; NotifyChunkToClients(entry); entry.ChunkLock.Set(); return chunk; } if ((chunk = Chunk.Load(coords, this)) != null) AddChunk(chunk); else if (create) chunk = _generator.ProvideChunk(coords.ChunkX, coords.ChunkZ, this); if(chunk == null) { threads = Interlocked.Decrement(ref entry.ThreadsWaiting); if (threads == 0) PendingChunks.TryRemove(coords.ChunkPackedCoords, out removedEntry); entry.ChunkLock.Set(); return chunk; } entry.ChunkRequested = chunk; entry.State = ChunkEntry.Initialized; threads = Interlocked.Decrement(ref entry.ThreadsWaiting); if (threads == 0) PendingChunks.TryRemove(coords.ChunkPackedCoords, out removedEntry); NotifyChunkToClients(entry); entry.ChunkLock.Set(); if (chunk != null) { chunk.InitGrowableCache(); ContainerFactory.LoadContainersFromDisk(chunk); } return chunk; } if (state == ChunkEntry.InProgress) { Interlocked.Increment(ref entry.ThreadsWaiting); if (!sync) entry.Requests.Enqueue(new ClientRequest { ClientRequesting = client }); else entry.ChunkLock.WaitOne(); int threads = Interlocked.Decrement(ref entry.ThreadsWaiting); if (threads == 0) PendingChunks.TryRemove(coords.ChunkPackedCoords, out removedEntry); if(!sync) { if (entry.State == ChunkEntry.Initialized) NotifyChunkToClients(entry); return null; } if (entry.State == ChunkEntry.Initialized) chunk = Chunks[coords]; else return null; Debug.Assert(chunk != null, "RETURNING NULL CHUNK"); return chunk; } chunk = Chunks[coords]; Debug.Assert(chunk != null, "RETURNING NULL CHUNK"); Debug.Assert(!PendingChunks.TryGetValue(coords.ChunkPackedCoords, out entry), "ENTRY INITIALIZED AND STILL LISTED"); return chunk; }