001    package net.minecraft.src;
002    
003    import java.io.IOException;
004    import java.util.ArrayList;
005    import java.util.HashSet;
006    import java.util.Iterator;
007    import java.util.List;
008    import java.util.Set;
009    
010    import cpw.mods.fml.common.registry.GameRegistry;
011    
012    public class ChunkProviderServer implements IChunkProvider
013    {
014        /**
015         * used by unload100OldestChunks to iterate the loadedChunkHashMap for unload (underlying assumption, first in,
016         * first out)
017         */
018        private Set chunksToUnload = new HashSet();
019        private Chunk defaultEmptyChunk;
020        private IChunkProvider currentChunkProvider;
021        private IChunkLoader currentChunkLoader;
022    
023        /**
024         * if this is false, the defaultEmptyChunk will be returned by the provider
025         */
026        public boolean loadChunkOnProvideRequest = true;
027        private LongHashMap loadedChunkHashMap = new LongHashMap();
028        private List loadedChunks = new ArrayList();
029        private WorldServer currentServer;
030    
031        public ChunkProviderServer(WorldServer par1WorldServer, IChunkLoader par2IChunkLoader, IChunkProvider par3IChunkProvider)
032        {
033            this.defaultEmptyChunk = new EmptyChunk(par1WorldServer, 0, 0);
034            this.currentServer = par1WorldServer;
035            this.currentChunkLoader = par2IChunkLoader;
036            this.currentChunkProvider = par3IChunkProvider;
037        }
038    
039        /**
040         * Checks to see if a chunk exists at x, y
041         */
042        public boolean chunkExists(int par1, int par2)
043        {
044            return this.loadedChunkHashMap.containsItem(ChunkCoordIntPair.chunkXZ2Int(par1, par2));
045        }
046    
047        /**
048         * marks chunk for unload by "unload100OldestChunks"  if there is no spawn point, or if the center of the chunk is
049         * outside 200 blocks (x or z) of the spawn
050         */
051        public void unloadChunksIfNotNearSpawn(int par1, int par2)
052        {
053            if (this.currentServer.provider.canRespawnHere())
054            {
055                ChunkCoordinates var3 = this.currentServer.getSpawnPoint();
056                int var4 = par1 * 16 + 8 - var3.posX;
057                int var5 = par2 * 16 + 8 - var3.posZ;
058                short var6 = 128;
059    
060                if (var4 < -var6 || var4 > var6 || var5 < -var6 || var5 > var6)
061                {
062                    this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(par1, par2)));
063                }
064            }
065            else
066            {
067                this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(par1, par2)));
068            }
069        }
070    
071        /**
072         * marks all chunks for unload, ignoring those near the spawn
073         */
074        public void unloadAllChunks()
075        {
076            Iterator var1 = this.loadedChunks.iterator();
077    
078            while (var1.hasNext())
079            {
080                Chunk var2 = (Chunk)var1.next();
081                this.unloadChunksIfNotNearSpawn(var2.xPosition, var2.zPosition);
082            }
083        }
084    
085        /**
086         * loads or generates the chunk at the chunk location specified
087         */
088        public Chunk loadChunk(int par1, int par2)
089        {
090            long var3 = ChunkCoordIntPair.chunkXZ2Int(par1, par2);
091            this.chunksToUnload.remove(Long.valueOf(var3));
092            Chunk var5 = (Chunk)this.loadedChunkHashMap.getValueByKey(var3);
093    
094            if (var5 == null)
095            {
096                var5 = this.safeLoadChunk(par1, par2);
097    
098                if (var5 == null)
099                {
100                    if (this.currentChunkProvider == null)
101                    {
102                        var5 = this.defaultEmptyChunk;
103                    }
104                    else
105                    {
106                        var5 = this.currentChunkProvider.provideChunk(par1, par2);
107                    }
108                }
109    
110                this.loadedChunkHashMap.add(var3, var5);
111                this.loadedChunks.add(var5);
112    
113                if (var5 != null)
114                {
115                    var5.onChunkLoad();
116                }
117    
118                var5.populateChunk(this, this, par1, par2);
119            }
120    
121            return var5;
122        }
123    
124        /**
125         * Will return back a chunk, if it doesn't exist and its not a MP client it will generates all the blocks for the
126         * specified chunk from the map seed and chunk seed
127         */
128        public Chunk provideChunk(int par1, int par2)
129        {
130            Chunk var3 = (Chunk)this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(par1, par2));
131            return var3 == null ? (!this.currentServer.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.defaultEmptyChunk : this.loadChunk(par1, par2)) : var3;
132        }
133    
134        /**
135         * used by loadChunk, but catches any exceptions if the load fails.
136         */
137        private Chunk safeLoadChunk(int par1, int par2)
138        {
139            if (this.currentChunkLoader == null)
140            {
141                return null;
142            }
143            else
144            {
145                try
146                {
147                    Chunk var3 = this.currentChunkLoader.loadChunk(this.currentServer, par1, par2);
148    
149                    if (var3 != null)
150                    {
151                        var3.lastSaveTime = this.currentServer.getWorldTime();
152                    }
153    
154                    return var3;
155                }
156                catch (Exception var4)
157                {
158                    var4.printStackTrace();
159                    return null;
160                }
161            }
162        }
163    
164        /**
165         * used by saveChunks, but catches any exceptions if the save fails.
166         */
167        private void safeSaveExtraChunkData(Chunk par1Chunk)
168        {
169            if (this.currentChunkLoader != null)
170            {
171                try
172                {
173                    this.currentChunkLoader.saveExtraChunkData(this.currentServer, par1Chunk);
174                }
175                catch (Exception var3)
176                {
177                    var3.printStackTrace();
178                }
179            }
180        }
181    
182        /**
183         * used by saveChunks, but catches any exceptions if the save fails.
184         */
185        private void safeSaveChunk(Chunk par1Chunk)
186        {
187            if (this.currentChunkLoader != null)
188            {
189                try
190                {
191                    par1Chunk.lastSaveTime = this.currentServer.getWorldTime();
192                    this.currentChunkLoader.saveChunk(this.currentServer, par1Chunk);
193                }
194                catch (IOException var3)
195                {
196                    var3.printStackTrace();
197                }
198                catch (MinecraftException var4)
199                {
200                    var4.printStackTrace();
201                }
202            }
203        }
204    
205        /**
206         * Populates chunk with ores etc etc
207         */
208        public void populate(IChunkProvider par1IChunkProvider, int par2, int par3)
209        {
210            Chunk var4 = this.provideChunk(par2, par3);
211    
212            if (!var4.isTerrainPopulated)
213            {
214                var4.isTerrainPopulated = true;
215    
216                if (this.currentChunkProvider != null)
217                {
218                    this.currentChunkProvider.populate(par1IChunkProvider, par2, par3);
219                    GameRegistry.generateWorld(par2, par3, currentServer, currentChunkProvider, par1IChunkProvider);
220                    var4.setChunkModified();
221                }
222            }
223        }
224    
225        /**
226         * Two modes of operation: if passed true, save all Chunks in one go.  If passed false, save up to two chunks.
227         * Return true if all chunks have been saved.
228         */
229        public boolean saveChunks(boolean par1, IProgressUpdate par2IProgressUpdate)
230        {
231            int var3 = 0;
232            Iterator var4 = this.loadedChunks.iterator();
233    
234            while (var4.hasNext())
235            {
236                Chunk var5 = (Chunk)var4.next();
237    
238                if (par1)
239                {
240                    this.safeSaveExtraChunkData(var5);
241                }
242    
243                if (var5.needsSaving(par1))
244                {
245                    this.safeSaveChunk(var5);
246                    var5.isModified = false;
247                    ++var3;
248    
249                    if (var3 == 24 && !par1)
250                    {
251                        return false;
252                    }
253                }
254            }
255    
256            if (par1)
257            {
258                if (this.currentChunkLoader == null)
259                {
260                    return true;
261                }
262    
263                this.currentChunkLoader.saveExtraData();
264            }
265    
266            return true;
267        }
268    
269        /**
270         * Unloads the 100 oldest chunks from memory, due to a bug with chunkSet.add() never being called it thinks the list
271         * is always empty and will not remove any chunks.
272         */
273        public boolean unload100OldestChunks()
274        {
275            if (!this.currentServer.canNotSave)
276            {
277                for (int var1 = 0; var1 < 100; ++var1)
278                {
279                    if (!this.chunksToUnload.isEmpty())
280                    {
281                        Long var2 = (Long)this.chunksToUnload.iterator().next();
282                        Chunk var3 = (Chunk)this.loadedChunkHashMap.getValueByKey(var2.longValue());
283                        var3.onChunkUnload();
284                        this.safeSaveChunk(var3);
285                        this.safeSaveExtraChunkData(var3);
286                        this.chunksToUnload.remove(var2);
287                        this.loadedChunkHashMap.remove(var2.longValue());
288                        this.loadedChunks.remove(var3);
289                    }
290                }
291    
292                if (this.currentChunkLoader != null)
293                {
294                    this.currentChunkLoader.chunkTick();
295                }
296            }
297    
298            return this.currentChunkProvider.unload100OldestChunks();
299        }
300    
301        /**
302         * Returns if the IChunkProvider supports saving.
303         */
304        public boolean canSave()
305        {
306            return !this.currentServer.canNotSave;
307        }
308    
309        /**
310         * Converts the instance data to a readable string.
311         */
312        public String makeString()
313        {
314            return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size();
315        }
316    
317        /**
318         * Returns a list of creatures of the specified type that can spawn at the given location.
319         */
320        public List getPossibleCreatures(EnumCreatureType par1EnumCreatureType, int par2, int par3, int par4)
321        {
322            return this.currentChunkProvider.getPossibleCreatures(par1EnumCreatureType, par2, par3, par4);
323        }
324    
325        /**
326         * Returns the location of the closest structure of the specified type. If not found returns null.
327         */
328        public ChunkPosition findClosestStructure(World par1World, String par2Str, int par3, int par4, int par5)
329        {
330            return this.currentChunkProvider.findClosestStructure(par1World, par2Str, par3, par4, par5);
331        }
332    
333        public int getLoadedChunkCount()
334        {
335            return this.loadedChunkHashMap.getNumHashElements();
336        }
337    }