2012-08-09 23:47:35 +00:00
package net.minecraftforge.common ;
2012-02-25 06:07:58 +00:00
2012-12-20 13:48:14 +00:00
import java.io.File ;
2012-08-15 06:59:15 +00:00
import java.util.ArrayList ;
2013-01-29 01:26:02 +00:00
import java.util.Arrays ;
2012-10-03 05:54:40 +00:00
import java.util.BitSet ;
2012-02-25 06:07:58 +00:00
import java.util.Hashtable ;
2013-01-27 15:43:35 +00:00
import java.util.List ;
import java.util.ListIterator ;
2012-09-22 05:43:54 +00:00
import java.util.Map ;
2012-08-11 01:15:10 +00:00
import java.util.Map.Entry ;
2013-01-27 15:43:35 +00:00
import java.util.concurrent.ConcurrentMap ;
2013-12-21 08:10:35 +00:00
import org.apache.logging.log4j.Level ;
2012-06-27 21:21:41 +00:00
2013-04-23 11:51:07 +00:00
import com.google.common.collect.HashMultiset ;
2013-01-27 15:43:35 +00:00
import com.google.common.collect.Lists ;
import com.google.common.collect.MapMaker ;
2013-04-23 11:51:07 +00:00
import com.google.common.collect.Multiset ;
2015-11-13 15:13:47 +00:00
2012-12-13 07:27:57 +00:00
import net.minecraft.nbt.NBTTagCompound ;
2012-08-11 01:15:10 +00:00
import net.minecraft.server.MinecraftServer ;
2012-12-13 07:27:57 +00: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 13:48:14 +00:00
import net.minecraft.world.storage.SaveHandler ;
2012-10-02 23:34:50 +00:00
import net.minecraftforge.event.world.WorldEvent ;
2015-11-13 15:13:47 +00:00
import net.minecraftforge.fml.common.FMLCommonHandler ;
import net.minecraftforge.fml.common.FMLLog ;
2012-02-25 06:07:58 +00:00
public class DimensionManager
{
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , Class < ? extends WorldProvider > > providers = new Hashtable < Integer , Class < ? extends WorldProvider > > ( ) ;
2012-02-25 06:07:58 +00:00
private static Hashtable < Integer , Boolean > spawnSettings = new Hashtable < Integer , Boolean > ( ) ;
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , WorldServer > worlds = new Hashtable < Integer , WorldServer > ( ) ;
2012-02-25 06:07:58 +00:00
private static boolean hasInit = false ;
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , Integer > dimensions = new Hashtable < Integer , Integer > ( ) ;
2012-10-02 23:34:50 +00:00
private static ArrayList < Integer > unloadQueue = new ArrayList < Integer > ( ) ;
2012-10-03 05:54:40 +00:00
private static BitSet dimensionMap = new BitSet ( Long . SIZE < < 4 ) ;
2013-01-27 15:43:35 +00:00
private static ConcurrentMap < World , World > weakWorldMap = new MapMaker ( ) . weakKeys ( ) . weakValues ( ) . < World , World > makeMap ( ) ;
2013-04-23 11:51:07 +00:00
private static Multiset < Integer > leakedWorlds = HashMultiset . create ( ) ;
2012-02-25 06:07:58 +00:00
2012-08-11 01:15:10 +00:00
public static boolean registerProviderType ( int id , Class < ? extends WorldProvider > provider , boolean keepLoaded )
2012-02-25 06:07:58 +00:00
{
2013-01-02 04:57:45 +00:00
if ( providers . containsKey ( id ) )
2012-02-25 06:07:58 +00:00
{
return false ;
}
providers . put ( id , provider ) ;
spawnSettings . put ( id , keepLoaded ) ;
return true ;
}
2013-01-29 01:26:02 +00:00
/ * *
2013-04-10 20:37:25 +00:00
* Unregisters a Provider type , and returns a array of all dimensions that are
2013-01-29 01:26:02 +00:00
* registered to this provider type .
2013-04-10 20:37:25 +00: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-29 01:26:02 +00:00
* world is attempted to load , else the loader will throw an exception .
2013-04-10 20:37:25 +00:00
*
2013-01-29 01:26:02 +00: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-25 06:07:58 +00:00
public static void init ( )
{
if ( hasInit )
{
return ;
}
2013-01-02 04:57:45 +00:00
hasInit = true ;
2012-08-11 01:15:10 +00: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-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static void registerDimension ( int id , int providerType )
2012-02-25 06:07:58 +00:00
{
2012-08-11 01:15:10 +00:00
if ( ! providers . containsKey ( providerType ) )
{
2012-10-02 23:34:50 +00:00
throw new IllegalArgumentException ( String . format ( " Failed to register dimension for id %d, provider type %d does not exist " , id , providerType ) ) ;
2012-08-11 01:15:10 +00:00
}
if ( dimensions . containsKey ( id ) )
{
2012-10-02 23:34:50 +00:00
throw new IllegalArgumentException ( String . format ( " Failed to register dimension for id %d, One is already registered " , id ) ) ;
2012-08-11 01:15:10 +00:00
}
dimensions . put ( id , providerType ) ;
2012-10-03 05:54:40 +00:00
if ( id > = 0 )
{
dimensionMap . set ( id ) ;
}
2012-10-02 23:34:50 +00: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-11 01:15:10 +00:00
}
2013-05-23 02:29:00 +00:00
public static boolean isDimensionRegistered ( int dim )
{
return dimensions . containsKey ( dim ) ;
}
2012-08-11 01:15:10 +00: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-25 06:07:58 +00:00
}
2012-08-14 22:03:40 +00:00
public static WorldProvider getProvider ( int dim )
{
return getWorld ( dim ) . provider ;
}
2013-01-27 15:43:35 +00: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 11:51:07 +00:00
leakedWorlds . add ( System . identityHashCode ( w ) ) ;
2013-01-27 15:43:35 +00:00
}
2013-04-23 11:51:07 +00:00
for ( World w : allWorlds )
2013-01-27 15:43:35 +00:00
{
2013-04-23 11:51:07 +00: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 15:43:35 +00:00
{
2013-04-23 11:51:07 +00:00
FMLLog . fine ( " The world %x (%s) may have leaked: seen %d times. \ n " , System . identityHashCode ( w ) , w . getWorldInfo ( ) . getWorldName ( ) , leakCount ) ;
2013-01-27 15:43:35 +00:00
}
}
}
return getIDs ( ) ;
}
2012-02-25 06:07:58 +00:00
public static Integer [ ] getIDs ( )
{
2012-10-02 23:34:50 +00:00
return worlds . keySet ( ) . toArray ( new Integer [ worlds . size ( ) ] ) ; //Only loaded dims, since usually used to cycle through loaded worlds
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static void setWorld ( int id , WorldServer world )
2012-02-25 06:07:58 +00:00
{
2013-09-05 17:16:01 +00:00
if ( world ! = null )
{
2012-10-02 23:34:50 +00:00
worlds . put ( id , world ) ;
2013-01-27 15:43:35 +00:00
weakWorldMap . put ( world , world ) ;
2012-10-02 23:34:50 +00:00
MinecraftServer . getServer ( ) . worldTickTimes . put ( id , new long [ 100 ] ) ;
2014-12-02 03:11:17 +00:00
FMLLog . info ( " Loading dimension %d (%s) (%s) " , id , world . getWorldInfo ( ) . getWorldName ( ) , world . getMinecraftServer ( ) ) ;
2013-09-05 17:16:01 +00:00
}
else
{
2012-10-02 23:34:50 +00:00
worlds . remove ( id ) ;
MinecraftServer . getServer ( ) . worldTickTimes . remove ( id ) ;
2012-10-04 04:07:59 +00:00
FMLLog . info ( " Unloading dimension %d " , id ) ;
2012-10-02 23:34:50 +00:00
}
2012-08-11 01:15:10 +00:00
2012-08-15 06:59:15 +00:00
ArrayList < WorldServer > tmp = new ArrayList < WorldServer > ( ) ;
2012-10-02 23:34:50 +00: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-11 01:15:10 +00:00
for ( Entry < Integer , WorldServer > entry : worlds . entrySet ( ) )
{
int dim = entry . getKey ( ) ;
if ( dim > = - 1 & & dim < = 1 )
{
continue ;
}
2012-08-15 06:59:15 +00:00
tmp . add ( entry . getValue ( ) ) ;
2012-08-11 01:15:10 +00:00
}
2013-01-02 04:57:45 +00:00
MinecraftServer . getServer ( ) . worldServers = tmp . toArray ( new WorldServer [ tmp . size ( ) ] ) ;
2012-10-02 23:34:50 +00:00
}
2015-01-09 07:02:25 +00:00
public static void initDimension ( int dim )
{
2012-10-02 23:34:50 +00:00
WorldServer overworld = getWorld ( 0 ) ;
2013-09-05 17:16:01 +00:00
if ( overworld = = null )
{
2012-10-02 23:34:50 +00:00
throw new RuntimeException ( " Cannot Hotload Dim: Overworld is not Loaded! " ) ;
}
2013-09-05 17:16:01 +00:00
try
{
2012-10-02 23:34:50 +00:00
DimensionManager . getProviderType ( dim ) ;
2013-09-05 17:16:01 +00:00
}
catch ( Exception e )
{
2012-10-02 23:34:50 +00:00
System . err . println ( " Cannot Hotload Dim: " + e . getMessage ( ) ) ;
2013-09-05 17:16:01 +00:00
return ; // If a provider hasn't been registered then we can't hotload the dim
2012-10-02 23:34:50 +00:00
}
2014-12-02 03:11:17 +00:00
MinecraftServer mcServer = overworld . getMinecraftServer ( ) ;
2012-10-02 23:34:50 +00:00
ISaveHandler savehandler = overworld . getSaveHandler ( ) ;
2015-11-27 03:38:21 +00:00
//WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo());
2012-10-02 23:34:50 +00:00
2015-01-09 07:02:25 +00:00
WorldServer world = ( dim = = 0 ? overworld : ( WorldServer ) ( new WorldServerMulti ( mcServer , savehandler , dim , overworld , mcServer . theProfiler ) . init ( ) ) ) ;
2012-10-02 23:34:50 +00:00
world . addWorldAccess ( new WorldManager ( mcServer , world ) ) ;
2012-10-04 21:35:22 +00:00
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Load ( world ) ) ;
2012-10-02 23:34:50 +00:00
if ( ! mcServer . isSinglePlayer ( ) )
{
world . getWorldInfo ( ) . setGameType ( mcServer . getGameType ( ) ) ;
}
Initial update to 1.8, Super beta. Most rendering related hooks are out due to major changes in 1.8.
Some notes:
Almost all int x, int y, int z parameters have been changed to BlockPos class
ForgeDirection has been removed, replaced by net.minecraft.util.EnumFacing.
All FML classes have moved from packet cpw.mods.fml to net.minecraftforge.fml
Fluid Rendering has been disabled for the time being, to be re-evaulated and a test mod created for it.
Minecraft now uses a Model based system for rendering blocks and Items. The intention is to expand the model format to better suit modder's needed once it is evaulated.
As such, The model loaders from Forge have been removed, to be replaced by expanding vanilla's model format.
Metadata has been extracted out in Minecraft to IBlockState, which holds a list of properties instead of magic number metadata. DO NOT listen to the fearmongering, you can do EVERYTHING with block states you could previously with metadata.
Stencil Bits are disabled entirely by for the main Display, Modders must enable and recreate the FrameBuffer if they wish to use Stencil Bits.
2014-11-26 03:56:35 +00:00
mcServer . setDifficultyForAllWorlds ( mcServer . getDifficulty ( ) ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static WorldServer getWorld ( int id )
2012-02-25 06:07:58 +00:00
{
return worlds . get ( id ) ;
}
2012-08-11 01:15:10 +00:00
public static WorldServer [ ] getWorlds ( )
2012-02-25 06:07:58 +00:00
{
2013-01-02 04:57:45 +00:00
return worlds . values ( ) . toArray ( new WorldServer [ worlds . size ( ) ] ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static boolean shouldLoadSpawn ( int dim )
2012-02-25 06:07:58 +00:00
{
2012-08-11 01:15:10 +00:00
int id = getProviderType ( dim ) ;
2012-12-04 10:01:01 +00:00
return spawnSettings . containsKey ( id ) & & spawnSettings . get ( id ) ;
2012-02-25 06:07:58 +00:00
}
static
{
init ( ) ;
}
2012-06-27 21:21:41 +00:00
2012-10-04 04:07:59 +00: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-11 01:15:10 +00:00
public static WorldProvider createProviderFor ( int dim )
{
try
{
if ( dimensions . containsKey ( dim ) )
{
2012-08-15 06:59:15 +00:00
WorldProvider provider = providers . get ( getProviderType ( dim ) ) . newInstance ( ) ;
provider . setDimension ( dim ) ;
return provider ;
2012-08-11 01:15:10 +00:00
}
else
{
2012-10-02 23:34:50 +00: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-11 01:15:10 +00:00
}
2012-09-22 05:43:54 +00:00
}
2012-08-11 01:15:10 +00:00
catch ( Exception e )
{
2013-12-21 08:10:35 +00:00
FMLCommonHandler . instance ( ) . getFMLLogger ( ) . log ( Level . ERROR , String . format ( " An error occured trying to create an instance of WorldProvider %d (%s) " ,
2012-09-22 05:43:54 +00:00
dim , providers . get ( getProviderType ( dim ) ) . getSimpleName ( ) ) , e ) ;
2012-08-11 01:15:10 +00:00
throw new RuntimeException ( e ) ;
}
}
2012-10-02 23:34:50 +00: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-24 04:36:59 +00:00
WorldServer w = worlds . get ( id ) ;
2012-10-02 23:34:50 +00:00
try {
2013-01-24 04:36:59 +00:00
if ( w ! = null )
{
w . saveAllChunks ( true , null ) ;
}
else
{
FMLLog . warning ( " Unexpected world unload - world %d is already unloaded " , id ) ;
}
2012-10-02 23:34:50 +00:00
} catch ( MinecraftException e ) {
e . printStackTrace ( ) ;
}
2013-01-24 04:36:59 +00:00
finally
{
if ( w ! = null )
{
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Unload ( w ) ) ;
w . flush ( ) ;
setWorld ( id , null ) ;
}
}
2012-10-02 23:34:50 +00:00
}
unloadQueue . clear ( ) ;
}
2012-10-03 05:54:40 +00: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-22 02:56:04 +00:00
* @return the next free dimension ID
2012-10-03 05:54:40 +00:00
* /
2012-10-02 23:34:50 +00:00
public static int getNextFreeDimId ( ) {
2012-10-03 05:54:40 +00: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 18:46:03 +00:00
dimensionMap . clear ( ) ;
2012-10-03 05:54:40 +00: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-02 23:34:50 +00:00
}
2012-12-20 13:48:14 +00:00
/ * *
2013-01-22 02:56:04 +00: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 13:48:14 +00:00
* /
public static File getCurrentSaveRootDirectory ( )
{
if ( DimensionManager . getWorld ( 0 ) ! = null )
{
2013-04-10 20:37:25 +00:00
return ( ( SaveHandler ) DimensionManager . getWorld ( 0 ) . getSaveHandler ( ) ) . getWorldDirectory ( ) ;
2012-12-20 13:48:14 +00:00
}
2013-01-22 15:07:41 +00:00
else if ( MinecraftServer . getServer ( ) ! = null )
{
MinecraftServer srv = MinecraftServer . getServer ( ) ;
SaveHandler saveHandler = ( SaveHandler ) srv . getActiveAnvilConverter ( ) . getSaveLoader ( srv . getFolderName ( ) , false ) ;
2013-04-10 20:37:25 +00:00
return saveHandler . getWorldDirectory ( ) ;
2013-01-22 15:07:41 +00:00
}
2012-12-20 13:48:14 +00:00
else
{
return null ;
}
}
Initial update to 1.8, Super beta. Most rendering related hooks are out due to major changes in 1.8.
Some notes:
Almost all int x, int y, int z parameters have been changed to BlockPos class
ForgeDirection has been removed, replaced by net.minecraft.util.EnumFacing.
All FML classes have moved from packet cpw.mods.fml to net.minecraftforge.fml
Fluid Rendering has been disabled for the time being, to be re-evaulated and a test mod created for it.
Minecraft now uses a Model based system for rendering blocks and Items. The intention is to expand the model format to better suit modder's needed once it is evaulated.
As such, The model loaders from Forge have been removed, to be replaced by expanding vanilla's model format.
Metadata has been extracted out in Minecraft to IBlockState, which holds a list of properties instead of magic number metadata. DO NOT listen to the fearmongering, you can do EVERYTHING with block states you could previously with metadata.
Stencil Bits are disabled entirely by for the main Display, Modders must enable and recreate the FrameBuffer if they wish to use Stencil Bits.
2014-11-26 03:56:35 +00:00
}