2016-06-23 03:49:47 +00:00
/ *
* Minecraft Forge
* Copyright ( c ) 2016 .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2 . 1
* of the License .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
* /
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 ;
2016-03-22 00:12:34 +00:00
import net.minecraft.world.DimensionType ;
2012-12-13 07:27:57 +00:00
import net.minecraft.world.MinecraftException ;
import net.minecraft.world.World ;
2016-05-18 10:29:10 +00:00
import net.minecraft.world.ServerWorldEventHandler ;
2012-12-13 07:27:57 +00:00
import net.minecraft.world.WorldProvider ;
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 , WorldServer > worlds = new Hashtable < Integer , WorldServer > ( ) ;
2012-02-25 06:07:58 +00:00
private static boolean hasInit = false ;
2016-03-22 00:12:34 +00:00
private static Hashtable < Integer , DimensionType > dimensions = new Hashtable < Integer , DimensionType > ( ) ;
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
2013-01-29 01:26:02 +00:00
/ * *
2016-03-22 00:12:34 +00:00
* Returns a list of dimensions associated with this DimensionType .
2013-01-29 01:26:02 +00:00
* /
2016-03-22 00:12:34 +00:00
public static int [ ] getDimensions ( DimensionType type )
2013-01-29 01:26:02 +00:00
{
int [ ] ret = new int [ dimensions . size ( ) ] ;
int x = 0 ;
2016-03-22 00:12:34 +00:00
for ( Map . Entry < Integer , DimensionType > ent : dimensions . entrySet ( ) )
2013-01-29 01:26:02 +00:00
{
2016-03-22 00:12:34 +00:00
if ( ent . getValue ( ) = = type )
2013-01-29 01:26:02 +00:00
{
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 ;
2016-03-22 00:12:34 +00:00
registerDimension ( 0 , DimensionType . OVERWORLD ) ;
registerDimension ( - 1 , DimensionType . NETHER ) ;
registerDimension ( 1 , DimensionType . THE_END ) ;
2012-02-25 06:07:58 +00:00
}
2016-03-22 00:12:34 +00:00
public static void registerDimension ( int id , DimensionType type )
2012-02-25 06:07:58 +00:00
{
2016-03-22 00:12:34 +00:00
DimensionType . getById ( type . getId ( ) ) ; //Check if type is invalid {will throw an error} No clue how it would be invalid tho...
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
}
2016-03-22 00:12:34 +00:00
dimensions . put ( id , type ) ;
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 ) ;
}
2016-03-22 00:12:34 +00:00
public static DimensionType getProviderType ( int dim )
2012-08-11 01:15:10 +00:00
{
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 )
{
2016-03-23 14:34:48 +00:00
FMLLog . fine ( " The world %x (%s) may have leaked: first encounter (5 occurrences). \ n " , System . identityHashCode ( w ) , w . getWorldInfo ( ) . getWorldName ( ) ) ;
2013-04-23 11:51:07 +00:00
}
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
}
2016-03-08 07:34:40 +00:00
public static void setWorld ( int id , WorldServer world , MinecraftServer server )
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 ) ;
2016-03-08 07:34:40 +00:00
server . 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 ) ;
2016-03-08 07:34:40 +00:00
server . 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
}
2016-03-08 07:34:40 +00:00
server . 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 ( ) ) ) ;
2016-05-18 10:29:10 +00:00
world . addEventListener ( new ServerWorldEventHandler ( 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
}
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 ) )
{
2016-03-22 00:12:34 +00:00
WorldProvider ret = getProviderType ( dim ) . createDimension ( ) ;
ret . setDimension ( dim ) ;
return ret ;
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 )
{
2016-03-23 14:34:48 +00:00
FMLCommonHandler . instance ( ) . getFMLLogger ( ) . log ( Level . ERROR , String . format ( " An error occurred trying to create an instance of WorldProvider %d (%s) " ,
2016-03-22 00:12:34 +00:00
dim , getProviderType ( dim ) ) , 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 ( ) ;
2016-03-08 07:34:40 +00:00
setWorld ( id , null , w . getMinecraftServer ( ) ) ;
2013-01-24 04:36:59 +00:00
}
}
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 )
{
2016-03-23 14:34:48 +00:00
return DimensionManager . getWorld ( 0 ) . getSaveHandler ( ) . getWorldDirectory ( ) ;
2016-03-08 07:34:40 +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 ( ) ;
2016-03-08 07:34:40 +00:00
} * /
2012-12-20 13:48:14 +00:00
else
{
return null ;
}
}
2016-03-08 07:34:40 +00:00
}