2016-06-22 23:49:47 -04:00
/*
* Minecraft Forge
2019-02-10 17:57:03 -05:00
* Copyright (c) 2016-2019.
2016-06-22 23:49:47 -04:00
*
* 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
*/
2019-02-14 18:49:40 -05:00
2012-08-09 16:47:35 -07:00
package net.minecraftforge.common ;
2012-02-24 22:07:58 -08:00
2019-05-23 16:02:15 -07:00
import java.io.IOException ;
2012-08-14 23:59:15 -07:00
import java.util.ArrayList ;
2019-02-14 15:08:53 -08:00
import java.util.Collections ;
import java.util.HashMap ;
2019-08-20 15:31:08 -07:00
import java.util.HashSet ;
2013-01-27 10:43:35 -05:00
import java.util.List ;
2012-09-22 01:43:54 -04:00
import java.util.Map ;
2019-08-20 15:31:08 -07:00
import java.util.Set ;
2013-01-27 10:43:35 -05:00
import java.util.concurrent.ConcurrentMap ;
2019-02-14 15:08:53 -08:00
import java.util.stream.Collectors ;
2013-12-21 00:10:35 -08:00
2018-06-27 19:23:23 +01:00
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap ;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap ;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps ;
import it.unimi.dsi.fastutil.ints.IntIterator ;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet ;
2018-04-01 08:54:22 +01:00
import it.unimi.dsi.fastutil.ints.IntSet ;
import it.unimi.dsi.fastutil.ints.IntSets ;
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
2019-02-14 15:08:53 -08:00
import io.netty.buffer.Unpooled ;
2019-05-23 16:02:15 -07:00
import net.minecraft.nbt.CompoundNBT ;
import net.minecraft.nbt.ListNBT ;
2019-02-14 15:08:53 -08:00
import net.minecraft.network.PacketBuffer ;
2012-08-10 18:15:10 -07:00
import net.minecraft.server.MinecraftServer ;
2019-08-20 15:31:08 -07:00
import net.minecraft.server.management.PlayerList ;
2019-02-14 15:08:53 -08:00
import net.minecraft.util.ResourceLocation ;
2019-05-23 16:02:15 -07:00
import net.minecraft.util.math.ChunkPos ;
import net.minecraft.util.registry.MutableRegistry ;
2012-12-12 23:27:57 -08:00
import net.minecraft.world.World ;
2019-12-11 03:27:07 +01:00
import net.minecraft.world.biome.IBiomeMagnifier ;
2019-05-23 16:02:15 -07:00
import net.minecraft.world.chunk.ChunkStatus ;
import net.minecraft.world.chunk.listener.IChunkStatusListener ;
2019-12-11 03:27:07 +01:00
import net.minecraft.world.server.ServerMultiWorld ;
2019-07-19 23:33:15 -04:00
import net.minecraft.world.server.ServerWorld ;
2018-09-14 12:30:56 -04:00
import net.minecraft.world.dimension.DimensionType ;
2019-02-14 15:08:53 -08:00
import net.minecraftforge.event.world.RegisterDimensionsEvent ;
2012-10-03 02:34:50 +03:00
import net.minecraftforge.event.world.WorldEvent ;
2019-03-30 14:46:30 -04:00
import net.minecraftforge.fml.StartupQuery ;
2019-09-16 20:14:21 -04:00
import net.minecraftforge.fml.server.ServerModLoader ;
2019-02-14 15:08:53 -08:00
import net.minecraftforge.registries.ClearableRegistry ;
import net.minecraftforge.registries.ForgeRegistries ;
import org.apache.commons.lang3.Validate ;
2018-06-21 15:37:32 -04:00
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
import org.apache.logging.log4j.Marker ;
import org.apache.logging.log4j.MarkerManager ;
2012-02-24 22:07:58 -08:00
2017-01-11 15:17:56 -08:00
import javax.annotation.Nullable ;
2012-02-24 22:07:58 -08:00
public class DimensionManager
{
2018-08-27 13:10:07 -04:00
private static final Logger LOGGER = LogManager . getLogger ();
2018-06-21 15:37:32 -04:00
private static final Marker DIMMGR = MarkerManager . getMarker ( "DIMS" );
2017-05-06 21:19:01 +02:00
2019-07-31 22:43:46 -07:00
private static final ClearableRegistry < DimensionType > REGISTRY = new ClearableRegistry <> ( new ResourceLocation ( "dimension_type" ), DimensionType . class );
2018-04-01 08:54:22 +01:00
2019-02-14 15:08:53 -08:00
private static final Int2ObjectMap < Data > dimensions = Int2ObjectMaps . synchronize ( new Int2ObjectLinkedOpenHashMap <> ());
2018-06-27 19:23:23 +01:00
private static final IntSet unloadQueue = IntSets . synchronize ( new IntLinkedOpenHashSet ());
2018-04-01 08:54:22 +01:00
private static final ConcurrentMap < World , World > weakWorldMap = new MapMaker (). weakKeys (). weakValues (). makeMap ();
private static final Multiset < Integer > leakedWorlds = HashMultiset . create ();
2019-02-14 15:08:53 -08:00
private static final Map < ResourceLocation , SavedEntry > savedEntries = new HashMap <> ();
2019-08-20 15:31:08 -07:00
private static volatile Set < World > playerWorlds = new HashSet <> ();
2012-02-24 22:07:58 -08:00
2019-10-02 21:54:02 -04:00
/**
* Register or get the existing dimension type for the given dimtype name.
*
* Dimensions already known to the save are loaded into the DimensionType map before the {@link RegisterDimensionsEvent} is fired.
* You can use this helper to get your existing dimension, or create it if it is not found.
*
* @param name Registry name
* @param type ModDimension type data
* @param data Extra data for the ModDimension
* @param hasSkyLight does this dimension have a skylight?
2019-12-11 03:27:07 +01:00
* @param magnifier The biome generation processor
2019-10-02 21:54:02 -04:00
* @return the DimensionType for the dimension.
*/
public static DimensionType registerOrGetDimension ( ResourceLocation name , ModDimension type , PacketBuffer data , boolean hasSkyLight ) {
return REGISTRY . getValue ( name ). orElseGet (() -> registerDimension ( name , type , data , hasSkyLight ));
}
2013-01-28 17:26:02 -08:00
/**
2019-02-14 15:08:53 -08:00
* Registers a real unique dimension, Should be called on server init, or when the dimension is created.
* On the client, the list will be reset/reloaded every time a InternalServer is refreshed.
*
* Forge will save this data and keep track of registered values, syncing automatically from the remote server.
*
* @param name Registry name for this new dimension.
* @param type Dimension Type.
* @param data Configuration data for this dimension, passed into
2019-10-02 21:54:02 -04:00
* @param hasSkyLight skylight for this dimension
2019-12-11 03:27:07 +01:00
* @param magnifier The biome generation processor
2019-10-02 21:54:02 -04:00
* @return the DimensionType for the dimension.
2013-01-28 17:26:02 -08:00
*/
2019-05-23 16:02:15 -07:00
public static DimensionType registerDimension ( ResourceLocation name , ModDimension type , PacketBuffer data , boolean hasSkyLight )
2013-01-28 17:26:02 -08:00
{
2019-04-10 04:34:34 +02:00
Validate . notNull ( name , "Can not register a dimension with null name" );
2019-05-23 16:02:15 -07:00
Validate . isTrue ( ! REGISTRY . containsKey ( name ), "Dimension: " + name + " Already registered" );
2019-02-14 15:08:53 -08:00
Validate . notNull ( type , "Can not register a null dimension type" );
2013-01-28 17:26:02 -08:00
2019-02-14 15:08:53 -08:00
int id = REGISTRY . getNextId ();
SavedEntry old = savedEntries . get ( name );
if ( old != null )
2012-02-24 22:07:58 -08:00
{
2019-02-14 15:08:53 -08:00
id = old . getId ();
if ( ! type . getRegistryName (). equals ( old . getType ()))
LOGGER . info ( DIMMGR , "Changing ModDimension for '{}' from '{}' to '{}'" , name . toString (), old . getType () == null ? null : old . getType (). toString (), type . getRegistryName (). toString ());
savedEntries . remove ( name );
2012-02-24 22:07:58 -08:00
}
2019-05-23 16:02:15 -07:00
@SuppressWarnings ( "deprecation" )
2019-12-11 03:27:07 +01:00
DimensionType instance = new DimensionType ( id , "" , name . getNamespace () + "/" + name . getPath (), type . getFactory (), hasSkyLight , type . getMagnifier (), type , data );
2019-06-08 10:45:28 -04:00
REGISTRY . register ( id , name , instance );
2019-02-14 15:08:53 -08:00
LOGGER . info ( DIMMGR , "Registered dimension {} of type {} and id {}" , name . toString (), type . getRegistryName (). toString (), id );
2019-03-06 21:01:12 -08:00
return instance ;
2012-02-24 22:07:58 -08:00
}
2019-02-14 15:08:53 -08:00
/**
* Configures if the dimension will stay loaded in memory even if all chunks are unloaded.
*
* @param dim The dimension
* @param value True to keep loaded, false to allow unloading
* @return The old value for this dimension.
*/
public static boolean keepLoaded ( DimensionType dim , boolean value )
2012-02-24 22:07:58 -08:00
{
2019-02-14 15:08:53 -08:00
Validate . notNull ( dim , "Dimension type must not be null" );
Data data = getData ( dim );
boolean ret = data . keepLoaded ;
data . keepLoaded = value ;
return ret ;
2012-10-03 02:34:50 +03:00
}
/**
2019-02-14 15:08:53 -08:00
* Determines if the dimension will stay loaded in memory even if all chunks are unloaded.
* @param dim The dimension
* @return True if the dimension will stay loaded with no chunks loaded.
2012-10-03 02:34:50 +03:00
*/
2019-02-14 15:08:53 -08:00
public static boolean keepLoaded ( DimensionType dim )
2012-10-03 02:34:50 +03:00
{
2019-02-14 15:08:53 -08:00
Validate . notNull ( dim , "Dimension type must not be null" );
Data data = dimensions . get ( dim . getId ());
return data == null ? false : data . keepLoaded ;
2012-08-10 18:15:10 -07:00
}
2019-02-14 15:08:53 -08:00
/**
* Retrieves the world from the server allowing for null return, and optionally resetting it's unload timer.
*
2019-08-20 15:31:08 -07:00
* @param server The server that controls this world.
2019-02-14 15:08:53 -08:00
* @param dim Dimension to load.
* @param resetUnloadDelay True to reset the unload timer, which is a delay that is used to prevent constant world loading/unloading cycle.
* @param forceLoad True to attempt to load the dimension if the server has it unloaded.
* @return The world, null if unloaded and not loadable.
*/
@Nullable
2019-05-23 16:02:15 -07:00
public static ServerWorld getWorld ( MinecraftServer server , DimensionType dim , boolean resetUnloadDelay , boolean forceLoad )
2013-05-22 22:29:00 -04:00
{
2019-02-14 15:08:53 -08:00
Validate . notNull ( server , "Must provide server when creating world" );
Validate . notNull ( dim , "Dimension type must not be null" );
2013-05-22 22:29:00 -04:00
2019-09-16 20:14:21 -04:00
if ( ServerModLoader . hasErrors ()) {
throw new RuntimeException ( "The server has failed to initialize correctly due to mod loading errors. Examine the crash report for more details." );
}
2019-03-30 14:46:30 -04:00
// If we're in the early stages of loading, we need to return null so CommandSource can work properly for command function
if ( StartupQuery . pendingQuery ()) {
return null ;
}
2019-02-14 15:08:53 -08:00
if ( resetUnloadDelay && unloadQueue . contains ( dim . getId ()))
getData ( dim ). ticksWaited = 0 ;
2012-02-24 22:07:58 -08:00
2019-02-14 15:08:53 -08:00
@SuppressWarnings ( "deprecation" )
2019-05-23 16:02:15 -07:00
ServerWorld ret = server . forgeGetWorldMap (). get ( dim );
2019-02-14 15:08:53 -08:00
if ( ret == null && forceLoad )
ret = initWorld ( server , dim );
return ret ;
2012-08-14 15:03:40 -07:00
}
2019-02-14 15:08:53 -08:00
//==========================================================================================================
// FORGE INTERNAL
//==========================================================================================================
2018-06-27 19:23:23 +01:00
2019-02-14 15:08:53 -08:00
public static void unregisterDimension ( int id )
2012-02-24 22:07:58 -08:00
{
2019-02-14 15:08:53 -08:00
Validate . isTrue ( dimensions . containsKey ( id ), String . format ( "Failed to unregister dimension for id %d; No provider registered" , id ));
dimensions . remove ( id );
2012-02-24 22:07:58 -08:00
}
2019-05-23 16:02:15 -07:00
public static DimensionType registerDimensionInternal ( int id , ResourceLocation name , ModDimension type , PacketBuffer data , boolean hasSkyLight )
2018-09-15 00:59:19 -07:00
{
2019-04-10 04:34:34 +02:00
Validate . notNull ( name , "Can not register a dimension with null name" );
2019-02-14 15:08:53 -08:00
Validate . notNull ( type , "Can not register a null dimension type" );
2019-05-23 16:02:15 -07:00
Validate . isTrue ( ! REGISTRY . containsKey ( name ), "Dimension: " + name + " Already registered" );
Validate . isTrue ( REGISTRY . getByValue ( id ) == null , "Dimension with id " + id + " already registered as name " + REGISTRY . getKey ( REGISTRY . getByValue ( id )));
2019-02-14 15:08:53 -08:00
2019-05-23 16:02:15 -07:00
@SuppressWarnings ( "deprecation" )
2019-12-11 03:27:07 +01:00
DimensionType instance = new DimensionType ( id , "" , name . getNamespace () + "/" + name . getPath (), type . getFactory (), hasSkyLight , type . getMagnifier (), type , data );
2019-06-08 10:45:28 -04:00
REGISTRY . register ( id , name , instance );
2019-02-14 15:08:53 -08:00
LOGGER . info ( DIMMGR , "Registered dimension {} of type {} and id {}" , name . toString (), type . getRegistryName (). toString (), id );
2019-03-06 21:01:12 -08:00
return instance ;
2018-09-15 00:59:19 -07:00
}
2019-02-14 15:08:53 -08:00
@SuppressWarnings ( "deprecation" )
2019-05-23 16:02:15 -07:00
public static ServerWorld initWorld ( MinecraftServer server , DimensionType dim )
2012-02-24 22:07:58 -08:00
{
2019-07-19 11:55:30 -04:00
Validate . isTrue ( dim != DimensionType . OVERWORLD , "Can not hotload overworld. This must be loaded at all times by main Server." );
2019-02-14 15:08:53 -08:00
Validate . notNull ( server , "Must provide server when creating world" );
Validate . notNull ( dim , "Must provide dimension when creating world" );
2012-08-10 18:15:10 -07:00
2019-07-19 11:55:30 -04:00
ServerWorld overworld = getWorld ( server , DimensionType . OVERWORLD , false , false );
2019-02-14 15:08:53 -08:00
Validate . notNull ( overworld , "Cannot Hotload Dim: Overworld is not Loaded!" );
2012-08-10 18:15:10 -07:00
2019-02-14 15:08:53 -08:00
@SuppressWarnings ( "resource" )
2019-06-21 22:42:16 -04:00
ServerWorld world = new ServerMultiWorld ( overworld , server , server . getBackgroundExecutor (), overworld . getSaveHandler (), dim , server . getProfiler (), new NoopChunkStatusListener ());
2019-02-14 15:08:53 -08:00
if ( ! server . isSinglePlayer ())
world . getWorldInfo (). setGameType ( server . getGameType ());
server . forgeGetWorldMap (). put ( dim , world );
2019-07-16 09:59:33 -07:00
server . markWorldsDirty ();
2012-10-03 02:34:50 +03:00
2012-10-04 17:35:22 -04:00
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Load ( world ));
2018-06-27 19:23:23 +01:00
2019-02-14 15:08:53 -08:00
return world ;
2018-04-01 08:54:22 +01:00
}
2019-05-23 16:02:15 -07:00
private static boolean canUnloadWorld ( ServerWorld world )
2018-04-01 08:54:22 +01:00
{
2019-07-27 02:35:06 -07:00
return world . getDimension (). getType () != DimensionType . OVERWORLD
2019-06-08 10:45:28 -04:00
&& world . getPlayers (). isEmpty ()
2019-07-27 02:35:06 -07:00
&& world . getForcedChunks (). isEmpty ()
2019-08-20 15:31:08 -07:00
&& ! getData ( world . getDimension (). getType ()). keepLoaded
&& ! playerWorlds . contains ( world );
2018-04-01 08:54:22 +01:00
}
/**
* Queues a dimension to unload, if it can be unloaded.
2019-07-31 01:39:05 +02:00
* @param world The world to unload
2017-05-06 21:19:01 +02:00
*/
2019-05-23 16:02:15 -07:00
public static void unloadWorld ( ServerWorld world )
2017-05-06 21:19:01 +02:00
{
2019-02-14 15:08:53 -08:00
if ( world == null || ! canUnloadWorld ( world ))
return ;
2018-04-01 08:54:22 +01:00
2019-02-14 15:08:53 -08:00
int id = world . getDimension (). getType (). getId ();
2018-06-27 19:23:23 +01:00
if ( unloadQueue . add ( id ))
2018-06-21 15:37:32 -04:00
LOGGER . debug ( DIMMGR , "Queueing dimension {} to unload" , id );
2012-10-03 02:34:50 +03:00
}
2019-02-14 15:08:53 -08:00
@SuppressWarnings ( "deprecation" )
public static void unloadWorlds ( MinecraftServer server , boolean checkLeaks )
2018-06-27 19:23:23 +01:00
{
IntIterator queueIterator = unloadQueue . iterator ();
while ( queueIterator . hasNext ())
{
int id = queueIterator . nextInt ();
2019-02-14 15:08:53 -08:00
DimensionType dim = DimensionType . getById ( id );
if ( dim == null )
{
LOGGER . warn ( DIMMGR , "Dimension with unknown type '{}' added to unload queue, removing" , id );
queueIterator . remove ();
continue ;
}
Data dimension = dimensions . computeIfAbsent ( id , k -> new Data ());
2019-02-10 16:38:14 -05:00
if ( dimension . ticksWaited < ForgeConfig . SERVER . dimensionUnloadQueueDelay . get ())
2017-05-06 21:19:01 +02:00
{
dimension . ticksWaited ++ ;
continue ;
}
2019-02-14 15:08:53 -08:00
2017-05-06 21:19:01 +02:00
queueIterator . remove ();
2019-02-14 15:08:53 -08:00
2019-05-23 16:02:15 -07:00
ServerWorld w = server . forgeGetWorldMap (). get ( dim );
2019-02-14 15:08:53 -08:00
2017-05-06 21:19:01 +02:00
dimension . ticksWaited = 0 ;
2018-04-01 08:54:22 +01:00
// Don't unload the world if the status changed
if ( w == null || ! canUnloadWorld ( w ))
2017-05-06 21:19:01 +02:00
{
2018-06-21 15:37:32 -04:00
LOGGER . debug ( DIMMGR , "Aborting unload for dimension {} as status changed" , id );
2017-05-06 21:19:01 +02:00
continue ;
}
try
{
2019-06-21 22:42:16 -04:00
w . save ( null , true , true );
2017-05-06 21:19:01 +02:00
}
2018-09-11 18:25:25 -07:00
catch ( Exception e )
2017-05-06 21:19:01 +02:00
{
2018-06-21 15:37:32 -04:00
LOGGER . error ( DIMMGR , "Caught an exception while saving all chunks:" , e );
2012-10-03 02:34:50 +03:00
}
2013-01-23 23:36:59 -05:00
finally
{
2017-05-06 21:19:01 +02:00
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Unload ( w ));
2019-07-27 02:35:06 -07:00
LOGGER . debug ( DIMMGR , "Unloading dimension {}" , id );
2019-05-23 16:02:15 -07:00
try {
w . close ();
} catch ( IOException e ) {
LOGGER . error ( "Exception closing the level" , e );
}
2019-02-14 15:08:53 -08:00
server . forgeGetWorldMap (). remove ( dim );
2019-07-16 09:59:33 -07:00
server . markWorldsDirty ();
2013-01-23 23:36:59 -05:00
}
2012-10-03 02:34:50 +03:00
}
2019-02-14 15:08:53 -08:00
if ( checkLeaks )
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
List < World > allWorlds = Lists . newArrayList ( weakWorldMap . keySet ());
allWorlds . removeAll ( server . forgeGetWorldMap (). values ());
allWorlds . stream (). map ( System :: identityHashCode ). forEach ( leakedWorlds :: add );
for ( World w : allWorlds )
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
int hash = System . identityHashCode ( w );
int leakCount = leakedWorlds . count ( hash );
if ( leakCount == 5 )
LOGGER . debug ( DIMMGR , "The world {} ({}) may have leaked: first encounter (5 occurrences).\n" , Integer . toHexString ( hash ), w . getWorldInfo (). getWorldName ());
else if ( leakCount % 5 == 0 )
LOGGER . debug ( DIMMGR , "The world {} ({}) may have leaked: seen {} times.\n" , Integer . toHexString ( hash ), w . getWorldInfo (). getWorldName (), leakCount );
2012-10-03 01:54:40 -04:00
}
}
}
2019-05-23 16:02:15 -07:00
public static void writeRegistry ( CompoundNBT data )
2012-10-03 01:54:40 -04:00
{
2019-05-23 16:02:15 -07:00
data . putInt ( "version" , 1 );
2019-02-14 15:08:53 -08:00
List < SavedEntry > list = new ArrayList <> ();
for ( DimensionType type : REGISTRY )
list . add ( new SavedEntry ( type ));
savedEntries . values (). forEach ( list :: add );
Collections . sort ( list , ( a , b ) -> a . id - b . id );
2019-05-23 16:02:15 -07:00
ListNBT lst = new ListNBT ();
2019-02-14 15:08:53 -08:00
list . forEach ( e -> lst . add ( e . write ()));
2019-06-08 10:45:28 -04:00
data . put ( "entries" , lst );
2012-10-03 01:54:40 -04:00
}
2019-05-23 16:02:15 -07:00
public static void readRegistry ( CompoundNBT data )
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
int version = data . getInt ( "version" );
if ( version != 1 )
throw new IllegalStateException ( "Attempted to load world with unknown Dimension data format: " + version );
LOGGER . debug ( DIMMGR , "Reading Dimension Entries." );
Map < ResourceLocation , DimensionType > vanilla = REGISTRY . stream (). filter ( DimensionType :: isVanilla ). collect ( Collectors . toMap ( REGISTRY :: getKey , v -> v ));
REGISTRY . clear ();
vanilla . forEach (( key , value ) -> {
LOGGER . debug ( DIMMGR , "Registering vanilla entry ID: {} Name: {} Value: {}" , value . getId () + 1 , key . toString (), value . toString ());
2019-06-08 10:45:28 -04:00
REGISTRY . register ( value . getId () + 1 , key , value );
2019-02-14 15:08:53 -08:00
});
savedEntries . clear ();
2019-07-27 02:35:06 -07:00
@SuppressWarnings ( "unused" )
2019-02-14 15:08:53 -08:00
boolean error = false ;
2019-05-23 16:02:15 -07:00
ListNBT list = data . getList ( "entries" , 10 );
2019-02-14 15:08:53 -08:00
for ( int x = 0 ; x < list . size (); x ++ )
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
SavedEntry entry = new SavedEntry ( list . getCompound ( x ));
if ( entry . type == null )
2012-10-03 01:54:40 -04:00
{
2019-05-23 16:02:15 -07:00
DimensionType type = REGISTRY . getOrDefault ( entry . name );
2019-02-14 15:08:53 -08:00
if ( type == null )
{
LOGGER . error ( DIMMGR , "Vanilla entry '{}' id {} in save file not found in registry." , entry . name . toString (), entry . id );
error = true ;
continue ;
}
int id = REGISTRY . getId ( type );
if ( id != entry . id )
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
LOGGER . error ( DIMMGR , "Vanilla entry '{}' id {} in save file has incorrect in {} in registry." , entry . name . toString (), entry . id , id );
error = true ;
continue ;
2012-10-03 01:54:40 -04:00
}
}
2019-02-14 15:08:53 -08:00
else
2012-10-03 01:54:40 -04:00
{
2019-02-14 15:08:53 -08:00
ModDimension mod = ForgeRegistries . MOD_DIMENSIONS . getValue ( entry . type );
if ( mod == null )
2012-10-03 01:54:40 -04:00
{
2019-07-31 01:39:05 +02:00
LOGGER . error ( DIMMGR , "Modded dimension entry '{}' id {} type {} in save file missing ModDimension." , entry . name . toString (), entry . id , entry . type . toString ());
2019-02-14 15:08:53 -08:00
savedEntries . put ( entry . name , entry );
continue ;
2012-10-03 01:54:40 -04:00
}
2019-05-23 16:02:15 -07:00
registerDimensionInternal ( entry . id , entry . name , mod , entry . data == null ? null : new PacketBuffer ( Unpooled . wrappedBuffer ( entry . data )), entry . skyLight ());
2012-10-03 01:54:40 -04:00
}
}
2019-07-31 22:43:46 -07:00
}
2019-02-14 15:08:53 -08:00
2019-07-31 22:43:46 -07:00
public static void fireRegister ()
{
2019-02-14 15:08:53 -08:00
MinecraftForge . EVENT_BUS . post ( new RegisterDimensionsEvent ( savedEntries ));
if ( ! savedEntries . isEmpty ())
{
savedEntries . values (). forEach ( entry -> {
LOGGER . warn ( DIMMGR , "Missing Dimension Name: '{}' Id: {} Type: '{}" , entry . name . toString (), entry . id , entry . type . toString ());
});
}
2012-10-03 02:34:50 +03:00
}
2012-12-20 08:48:14 -05:00
2019-02-14 15:08:53 -08:00
@Deprecated //Forge: Internal use only.
2019-05-23 16:02:15 -07:00
public static MutableRegistry < DimensionType > getRegistry ()
2019-02-14 15:08:53 -08:00
{
return REGISTRY ;
}
private static Data getData ( DimensionType dim )
{
return dimensions . computeIfAbsent ( dim . getId (), k -> new Data ());
}
private static class Data
{
int ticksWaited = 0 ;
boolean keepLoaded = false ;
}
public static class SavedEntry
2012-12-20 08:48:14 -05:00
{
2019-02-14 15:08:53 -08:00
int id ;
ResourceLocation name ;
ResourceLocation type ;
byte [] data ;
2019-05-23 16:02:15 -07:00
boolean skyLight ;
2019-02-14 15:08:53 -08:00
public int getId ()
{
return id ;
}
public ResourceLocation getName ()
{
return name ;
}
@Nullable
public ResourceLocation getType ()
{
return type ;
}
@Nullable
public byte [] getData ()
{
return data ;
}
2019-05-23 16:02:15 -07:00
public boolean skyLight ()
{
return this . skyLight ;
}
private SavedEntry ( CompoundNBT data )
2012-12-20 08:48:14 -05:00
{
2019-02-14 15:08:53 -08:00
this . id = data . getInt ( "id" );
this . name = new ResourceLocation ( data . getString ( "name" ));
this . type = data . contains ( "type" , 8 ) ? new ResourceLocation ( data . getString ( "type" )) : null ;
this . data = data . contains ( "data" , 7 ) ? data . getByteArray ( "data" ) : null ;
2019-08-15 19:17:28 +02:00
this . skyLight = data . contains ( "sky_light" , 99 ) ? data . getBoolean ( "sky_light" ) : true ;
2019-02-14 15:08:53 -08:00
}
private SavedEntry ( DimensionType data )
2013-01-22 10:07:41 -05:00
{
2019-02-14 15:08:53 -08:00
this . id = REGISTRY . getId ( data );
this . name = REGISTRY . getKey ( data );
if ( data . getModType () != null )
this . type = data . getModType (). getRegistryName ();
if ( data . getData () != null )
this . data = data . getData (). array ();
2020-02-25 22:45:14 -05:00
this . skyLight = data . hasSkyLight ();
2019-02-14 15:08:53 -08:00
}
2019-05-23 16:02:15 -07:00
private CompoundNBT write ()
2012-12-20 08:48:14 -05:00
{
2019-05-23 16:02:15 -07:00
CompoundNBT ret = new CompoundNBT ();
ret . putInt ( "id" , id );
ret . putString ( "name" , name . toString ());
2019-02-14 15:08:53 -08:00
if ( type != null )
2019-05-23 16:02:15 -07:00
ret . putString ( "type" , type . toString ());
2019-02-14 15:08:53 -08:00
if ( data != null )
2019-05-23 16:02:15 -07:00
ret . putByteArray ( "data" , data );
2019-08-15 19:17:28 +02:00
ret . putBoolean ( "sky_light" , skyLight );
2019-02-14 15:08:53 -08:00
return ret ;
2012-12-20 08:48:14 -05:00
}
}
2019-05-23 16:02:15 -07:00
private static class NoopChunkStatusListener implements IChunkStatusListener
{
2019-07-19 11:55:30 -04:00
@Override public void start ( ChunkPos center ) { }
2020-05-15 12:17:25 -07:00
@Override public void statusChanged ( ChunkPos chunkPosition , ChunkStatus newStatus ) { }
2019-07-19 11:55:30 -04:00
@Override public void stop () { }
2019-05-23 16:02:15 -07:00
}
2019-08-20 15:31:08 -07:00
public static boolean rebuildPlayerMap ( PlayerList players , boolean changed )
{
playerWorlds = players . getPlayers (). stream (). map ( e -> e . world ). collect ( Collectors . toSet ());
return changed ;
}
2016-03-07 23:34:40 -08:00
}