2012-08-09 16:47:35 -07:00
package net.minecraftforge.common ;
2012-02-24 22:07:58 -08:00
2012-12-20 08:48:14 -05:00
import java.io.File ;
2012-08-14 23:59:15 -07:00
import java.util.ArrayList ;
2013-01-28 17:26:02 -08:00
import java.util.Arrays ;
2012-10-03 01:54:40 -04:00
import java.util.BitSet ;
2012-02-24 22:07:58 -08:00
import java.util.Hashtable ;
2013-01-27 10:43:35 -05:00
import java.util.List ;
import java.util.ListIterator ;
2012-09-22 01:43:54 -04:00
import java.util.Map ;
2012-08-10 18:15:10 -07:00
import java.util.Map.Entry ;
2013-01-27 10:43:35 -05:00
import java.util.concurrent.ConcurrentMap ;
2013-12-21 00:10:35 -08:00
import org.apache.logging.log4j.Level ;
2012-06-27 17:21:41 -04:00
2013-04-23 07:51:07 -04:00
import com.google.common.collect.HashMultiset ;
2013-01-27 10:43:35 -05:00
import com.google.common.collect.Lists ;
import com.google.common.collect.MapMaker ;
2013-04-23 07:51:07 -04:00
import com.google.common.collect.Multiset ;
2015-11-13 10:13:47 -05:00
2012-12-12 23:27:57 -08:00
import net.minecraft.nbt.NBTTagCompound ;
2012-08-10 18:15:10 -07:00
import net.minecraft.server.MinecraftServer ;
2012-12-12 23:27:57 -08:00
import net.minecraft.world.MinecraftException ;
import net.minecraft.world.World ;
import net.minecraft.world.WorldManager ;
import net.minecraft.world.WorldProvider ;
import net.minecraft.world.WorldProviderEnd ;
import net.minecraft.world.WorldProviderHell ;
import net.minecraft.world.WorldProviderSurface ;
import net.minecraft.world.WorldServer ;
import net.minecraft.world.WorldServerMulti ;
import net.minecraft.world.storage.ISaveHandler ;
2012-12-20 08:48:14 -05:00
import net.minecraft.world.storage.SaveHandler ;
2012-10-03 02:34:50 +03:00
import net.minecraftforge.event.world.WorldEvent ;
2015-11-13 10:13:47 -05:00
import net.minecraftforge.fml.common.FMLCommonHandler ;
import net.minecraftforge.fml.common.FMLLog ;
2012-02-24 22:07:58 -08:00
public class DimensionManager
{
2012-08-10 18:15:10 -07:00
private static Hashtable < Integer , Class <? extends WorldProvider >> providers = new Hashtable < Integer , Class <? extends WorldProvider >> ();
2012-02-24 22:07:58 -08:00
private static Hashtable < Integer , Boolean > spawnSettings = new Hashtable < Integer , Boolean > ();
2012-08-10 18:15:10 -07:00
private static Hashtable < Integer , WorldServer > worlds = new Hashtable < Integer , WorldServer > ();
2012-02-24 22:07:58 -08:00
private static boolean hasInit = false ;
2012-08-10 18:15:10 -07:00
private static Hashtable < Integer , Integer > dimensions = new Hashtable < Integer , Integer > ();
2012-10-03 02:34:50 +03:00
private static ArrayList < Integer > unloadQueue = new ArrayList < Integer > ();
2012-10-03 01:54:40 -04:00
private static BitSet dimensionMap = new BitSet ( Long . SIZE << 4 );
2013-01-27 10:43:35 -05:00
private static ConcurrentMap < World , World > weakWorldMap = new MapMaker (). weakKeys (). weakValues (). < World , World > makeMap ();
2013-04-23 07:51:07 -04:00
private static Multiset < Integer > leakedWorlds = HashMultiset . create ();
2012-02-24 22:07:58 -08:00
2012-08-10 18:15:10 -07:00
public static boolean registerProviderType ( int id , Class <? extends WorldProvider > provider , boolean keepLoaded )
2012-02-24 22:07:58 -08:00
{
2013-01-01 20:57:45 -08:00
if ( providers . containsKey ( id ))
2012-02-24 22:07:58 -08:00
{
return false ;
}
providers . put ( id , provider );
spawnSettings . put ( id , keepLoaded );
return true ;
}
2013-01-28 17:26:02 -08:00
/**
2013-04-10 16:37:25 -04:00
* Unregisters a Provider type, and returns a array of all dimensions that are
2013-01-28 17:26:02 -08:00
* registered to this provider type.
2013-04-10 16:37:25 -04:00
* If the return size is greater then 0, it is required that the caller either
* change those dimensions's registered type, or replace this type before the
2013-01-28 17:26:02 -08:00
* world is attempted to load, else the loader will throw an exception.
2013-04-10 16:37:25 -04:00
*
2013-01-28 17:26:02 -08:00
* @param id The provider type ID to unreigster
* @return An array containing all dimension IDs still registered to this provider type.
*/
public static int [] unregisterProviderType ( int id )
{
if ( ! providers . containsKey ( id ))
{
return new int [ 0 ] ;
}
providers . remove ( id );
spawnSettings . remove ( id );
int [] ret = new int [ dimensions . size () ] ;
int x = 0 ;
for ( Map . Entry < Integer , Integer > ent : dimensions . entrySet ())
{
if ( ent . getValue () == id )
{
ret [ x ++] = ent . getKey ();
}
}
return Arrays . copyOf ( ret , x );
}
2012-02-24 22:07:58 -08:00
public static void init ()
{
if ( hasInit )
{
return ;
}
2013-01-01 20:57:45 -08:00
hasInit = true ;
2012-08-10 18:15:10 -07:00
registerProviderType ( 0 , WorldProviderSurface . class , true );
registerProviderType ( - 1 , WorldProviderHell . class , true );
registerProviderType ( 1 , WorldProviderEnd . class , false );
registerDimension ( 0 , 0 );
registerDimension ( - 1 , - 1 );
registerDimension ( 1 , 1 );
2012-02-24 22:07:58 -08:00
}
2012-08-10 18:15:10 -07:00
public static void registerDimension ( int id , int providerType )
2012-02-24 22:07:58 -08:00
{
2012-08-10 18:15:10 -07:00
if ( ! providers . containsKey ( providerType ))
{
2012-10-03 02:34:50 +03:00
throw new IllegalArgumentException ( String . format ( "Failed to register dimension for id %d, provider type %d does not exist" , id , providerType ));
2012-08-10 18:15:10 -07:00
}
if ( dimensions . containsKey ( id ))
{
2012-10-03 02:34:50 +03:00
throw new IllegalArgumentException ( String . format ( "Failed to register dimension for id %d, One is already registered" , id ));
2012-08-10 18:15:10 -07:00
}
dimensions . put ( id , providerType );
2012-10-03 01:54:40 -04:00
if ( id >= 0 )
{
dimensionMap . set ( id );
}
2012-10-03 02:34:50 +03:00
}
/**
* For unregistering a dimension when the save is changed (disconnected from a server or loaded a new save
*/
public static void unregisterDimension ( int id )
{
if ( ! dimensions . containsKey ( id ))
{
throw new IllegalArgumentException ( String . format ( "Failed to unregister dimension for id %d; No provider registered" , id ));
}
dimensions . remove ( id );
2012-08-10 18:15:10 -07:00
}
2013-05-22 22:29:00 -04:00
public static boolean isDimensionRegistered ( int dim )
{
return dimensions . containsKey ( dim );
}
2012-08-10 18:15:10 -07:00
public static int getProviderType ( int dim )
{
if ( ! dimensions . containsKey ( dim ))
{
throw new IllegalArgumentException ( String . format ( "Could not get provider type for dimension %d, does not exist" , dim ));
}
return dimensions . get ( dim );
2012-02-24 22:07:58 -08:00
}
2012-08-14 15:03:40 -07:00
public static WorldProvider getProvider ( int dim )
{
return getWorld ( dim ). provider ;
}
2013-01-27 10:43:35 -05:00
public static Integer [] getIDs ( boolean check )
{
if ( check )
{
List < World > allWorlds = Lists . newArrayList ( weakWorldMap . keySet ());
allWorlds . removeAll ( worlds . values ());
for ( ListIterator < World > li = allWorlds . listIterator (); li . hasNext (); )
{
World w = li . next ();
2013-04-23 07:51:07 -04:00
leakedWorlds . add ( System . identityHashCode ( w ));
2013-01-27 10:43:35 -05:00
}
2013-04-23 07:51:07 -04:00
for ( World w : allWorlds )
2013-01-27 10:43:35 -05:00
{
2013-04-23 07:51:07 -04:00
int leakCount = leakedWorlds . count ( System . identityHashCode ( w ));
if ( leakCount == 5 )
{
FMLLog . fine ( "The world %x (%s) may have leaked: first encounter (5 occurences).\n" , System . identityHashCode ( w ), w . getWorldInfo (). getWorldName ());
}
else if ( leakCount % 5 == 0 )
2013-01-27 10:43:35 -05:00
{
2013-04-23 07:51:07 -04:00
FMLLog . fine ( "The world %x (%s) may have leaked: seen %d times.\n" , System . identityHashCode ( w ), w . getWorldInfo (). getWorldName (), leakCount );
2013-01-27 10:43:35 -05:00
}
}
}
return getIDs ();
}
2012-02-24 22:07:58 -08:00
public static Integer [] getIDs ()
{
2012-10-03 02:34:50 +03:00
return worlds . keySet (). toArray ( new Integer [ worlds . size () ] ); //Only loaded dims, since usually used to cycle through loaded worlds
2012-02-24 22:07:58 -08:00
}
2012-08-10 18:15:10 -07:00
public static void setWorld ( int id , WorldServer world )
2012-02-24 22:07:58 -08:00
{
2013-09-05 13:16:01 -04:00
if ( world != null )
{
2012-10-03 02:34:50 +03:00
worlds . put ( id , world );
2013-01-27 10:43:35 -05:00
weakWorldMap . put ( world , world );
2012-10-03 02:34:50 +03:00
MinecraftServer . getServer (). worldTickTimes . put ( id , new long [ 100 ] );
2014-12-01 19:11:17 -08:00
FMLLog . info ( "Loading dimension %d (%s) (%s)" , id , world . getWorldInfo (). getWorldName (), world . getMinecraftServer ());
2013-09-05 13:16:01 -04:00
}
else
{
2012-10-03 02:34:50 +03:00
worlds . remove ( id );
MinecraftServer . getServer (). worldTickTimes . remove ( id );
2012-10-04 00:07:59 -04:00
FMLLog . info ( "Unloading dimension %d" , id );
2012-10-03 02:34:50 +03:00
}
2012-08-10 18:15:10 -07:00
2012-08-14 23:59:15 -07:00
ArrayList < WorldServer > tmp = new ArrayList < WorldServer > ();
2012-10-03 02:34:50 +03:00
if ( worlds . get ( 0 ) != null )
tmp . add ( worlds . get ( 0 ));
if ( worlds . get ( - 1 ) != null )
tmp . add ( worlds . get ( - 1 ));
if ( worlds . get ( 1 ) != null )
tmp . add ( worlds . get ( 1 ));
2012-08-10 18:15:10 -07:00
for ( Entry < Integer , WorldServer > entry : worlds . entrySet ())
{
int dim = entry . getKey ();
if ( dim >= - 1 && dim <= 1 )
{
continue ;
}
2012-08-14 23:59:15 -07:00
tmp . add ( entry . getValue ());
2012-08-10 18:15:10 -07:00
}
2013-01-01 20:57:45 -08:00
MinecraftServer . getServer (). worldServers = tmp . toArray ( new WorldServer [ tmp . size () ] );
2012-10-03 02:34:50 +03:00
}
2015-01-08 23:02:25 -08:00
public static void initDimension ( int dim )
{
2012-10-03 02:34:50 +03:00
WorldServer overworld = getWorld ( 0 );
2013-09-05 13:16:01 -04:00
if ( overworld == null )
{
2012-10-03 02:34:50 +03:00
throw new RuntimeException ( "Cannot Hotload Dim: Overworld is not Loaded!" );
}
2013-09-05 13:16:01 -04:00
try
{
2012-10-03 02:34:50 +03:00
DimensionManager . getProviderType ( dim );
2013-09-05 13:16:01 -04:00
}
catch ( Exception e )
{
2012-10-03 02:34:50 +03:00
System . err . println ( "Cannot Hotload Dim: " + e . getMessage ());
2013-09-05 13:16:01 -04:00
return ; // If a provider hasn't been registered then we can't hotload the dim
2012-10-03 02:34:50 +03:00
}
2014-12-01 19:11:17 -08:00
MinecraftServer mcServer = overworld . getMinecraftServer ();
2012-10-03 02:34:50 +03:00
ISaveHandler savehandler = overworld . getSaveHandler ();
2015-11-26 19:38:21 -08:00
//WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo());
2012-10-03 02:34:50 +03:00
2015-01-08 23:02:25 -08:00
WorldServer world = ( dim == 0 ? overworld : ( WorldServer )( new WorldServerMulti ( mcServer , savehandler , dim , overworld , mcServer . theProfiler ). init ()));
2012-10-03 02:34:50 +03:00
world . addWorldAccess ( new WorldManager ( mcServer , world ));
2012-10-04 17:35:22 -04:00
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Load ( world ));
2012-10-03 02:34:50 +03:00
if ( ! mcServer . isSinglePlayer ())
{
world . getWorldInfo (). setGameType ( mcServer . getGameType ());
}
2014-11-25 19:56:35 -08:00
mcServer . setDifficultyForAllWorlds ( mcServer . getDifficulty ());
2012-02-24 22:07:58 -08:00
}
2012-08-10 18:15:10 -07:00
public static WorldServer getWorld ( int id )
2012-02-24 22:07:58 -08:00
{
return worlds . get ( id );
}
2012-08-10 18:15:10 -07:00
public static WorldServer [] getWorlds ()
2012-02-24 22:07:58 -08:00
{
2013-01-01 20:57:45 -08:00
return worlds . values (). toArray ( new WorldServer [ worlds . size () ] );
2012-02-24 22:07:58 -08:00
}
2012-08-10 18:15:10 -07:00
public static boolean shouldLoadSpawn ( int dim )
2012-02-24 22:07:58 -08:00
{
2012-08-10 18:15:10 -07:00
int id = getProviderType ( dim );
2012-12-04 02:01:01 -08:00
return spawnSettings . containsKey ( id ) && spawnSettings . get ( id );
2012-02-24 22:07:58 -08:00
}
static
{
init ();
}
2012-06-27 17:21:41 -04:00
2012-10-04 00:07:59 -04:00
/**
* Not public API: used internally to get dimensions that should load at
* server startup
*/
public static Integer [] getStaticDimensionIDs ()
{
return dimensions . keySet (). toArray ( new Integer [ dimensions . keySet (). size () ] );
}
2012-08-10 18:15:10 -07:00
public static WorldProvider createProviderFor ( int dim )
{
try
{
if ( dimensions . containsKey ( dim ))
{
2012-08-14 23:59:15 -07:00
WorldProvider provider = providers . get ( getProviderType ( dim )). newInstance ();
provider . setDimension ( dim );
return provider ;
2012-08-10 18:15:10 -07:00
}
else
{
2012-10-03 02:34:50 +03:00
throw new RuntimeException ( String . format ( "No WorldProvider bound for dimension %d" , dim )); //It's going to crash anyway at this point. Might as well be informative
2012-08-10 18:15:10 -07:00
}
2012-09-22 01:43:54 -04:00
}
2012-08-10 18:15:10 -07:00
catch ( Exception e )
{
2013-12-21 00:10:35 -08:00
FMLCommonHandler . instance (). getFMLLogger (). log ( Level . ERROR , String . format ( "An error occured trying to create an instance of WorldProvider %d (%s)" ,
2012-09-22 01:43:54 -04:00
dim , providers . get ( getProviderType ( dim )). getSimpleName ()), e );
2012-08-10 18:15:10 -07:00
throw new RuntimeException ( e );
}
}
2012-10-03 02:34:50 +03:00
public static void unloadWorld ( int id ) {
unloadQueue . add ( id );
}
/*
* To be called by the server at the appropriate time, do not call from mod code.
*/
public static void unloadWorlds ( Hashtable < Integer , long []> worldTickTimes ) {
for ( int id : unloadQueue ) {
2013-01-23 23:36:59 -05:00
WorldServer w = worlds . get ( id );
2012-10-03 02:34:50 +03:00
try {
2013-01-23 23:36:59 -05:00
if ( w != null )
{
w . saveAllChunks ( true , null );
}
else
{
FMLLog . warning ( "Unexpected world unload - world %d is already unloaded" , id );
}
2012-10-03 02:34:50 +03:00
} catch ( MinecraftException e ) {
e . printStackTrace ();
}
2013-01-23 23:36:59 -05:00
finally
{
if ( w != null )
{
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Unload ( w ));
w . flush ();
setWorld ( id , null );
}
}
2012-10-03 02:34:50 +03:00
}
unloadQueue . clear ();
}
2012-10-03 01:54:40 -04:00
/**
* Return the next free dimension ID. Note: you are not guaranteed a contiguous
* block of free ids. Always call for each individual ID you wish to get.
2013-01-21 21:56:04 -05:00
* @return the next free dimension ID
2012-10-03 01:54:40 -04:00
*/
2012-10-03 02:34:50 +03:00
public static int getNextFreeDimId () {
2012-10-03 01:54:40 -04:00
int next = 0 ;
while ( true )
{
next = dimensionMap . nextClearBit ( next );
if ( dimensions . containsKey ( next ))
{
dimensionMap . set ( next );
}
else
{
return next ;
}
}
}
public static NBTTagCompound saveDimensionDataMap ()
{
int [] data = new int [ ( dimensionMap . length () + Integer . SIZE - 1 ) / Integer . SIZE ] ;
NBTTagCompound dimMap = new NBTTagCompound ();
for ( int i = 0 ; i < data . length ; i ++ )
{
int val = 0 ;
for ( int j = 0 ; j < Integer . SIZE ; j ++ )
{
val |= dimensionMap . get ( i * Integer . SIZE + j ) ? ( 1 << j ) : 0 ;
}
data [ i ] = val ;
}
dimMap . setIntArray ( "DimensionArray" , data );
return dimMap ;
}
public static void loadDimensionDataMap ( NBTTagCompound compoundTag )
{
2014-05-12 11:46:03 -07:00
dimensionMap . clear ();
2012-10-03 01:54:40 -04:00
if ( compoundTag == null )
{
for ( Integer id : dimensions . keySet ())
{
if ( id >= 0 )
{
dimensionMap . set ( id );
}
}
}
else
{
int [] intArray = compoundTag . getIntArray ( "DimensionArray" );
for ( int i = 0 ; i < intArray . length ; i ++ )
{
for ( int j = 0 ; j < Integer . SIZE ; j ++ )
{
dimensionMap . set ( i * Integer . SIZE + j , ( intArray [ i ] & ( 1 << j )) != 0 );
}
}
}
2012-10-03 02:34:50 +03:00
}
2012-12-20 08:48:14 -05:00
/**
2013-01-21 21:56:04 -05:00
* Return the current root directory for the world save. Accesses getSaveHandler from the overworld
* @return the root directory of the save
2012-12-20 08:48:14 -05:00
*/
public static File getCurrentSaveRootDirectory ()
{
if ( DimensionManager . getWorld ( 0 ) != null )
{
2013-04-10 16:37:25 -04:00
return (( SaveHandler ) DimensionManager . getWorld ( 0 ). getSaveHandler ()). getWorldDirectory ();
2012-12-20 08:48:14 -05:00
}
2013-01-22 10:07:41 -05:00
else if ( MinecraftServer . getServer () != null )
{
MinecraftServer srv = MinecraftServer . getServer ();
SaveHandler saveHandler = ( SaveHandler ) srv . getActiveAnvilConverter (). getSaveLoader ( srv . getFolderName (), false );
2013-04-10 16:37:25 -04:00
return saveHandler . getWorldDirectory ();
2013-01-22 10:07:41 -05:00
}
2012-12-20 08:48:14 -05:00
else
{
return null ;
}
}
2014-11-25 19:56:35 -08:00
}