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