/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. * * Applied Energistics 2 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, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 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 Applied Energistics 2. If not, see . */ package appeng.core; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.WeakHashMap; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import com.google.common.base.Optional; import com.mojang.authlib.GameProfile; import appeng.api.util.WorldCoord; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketNewStorageDimension; import appeng.hooks.TickHandler; import appeng.hooks.TickHandler.PlayerColor; import appeng.me.GridStorage; import appeng.me.GridStorageSearch; import appeng.services.CompassService; public class WorldSettings extends Configuration { private static final String SPAWNDATA_FOLDER = "spawndata"; private static final String COMPASS_FOLDER = "compass"; private static WorldSettings instance; private final List storageCellDims = new ArrayList(); private final File spawnDataFolder; private final CompassService compass; private final PlayerMappings mappings; private final Map> loadedStorage = new WeakHashMap>(); private long lastGridStorage; private int lastPlayer; public WorldSettings( File aeFolder ) { super( new File( aeFolder.getPath(), "settings.cfg" ) ); this.spawnDataFolder = new File( aeFolder, SPAWNDATA_FOLDER ); this.compass = new CompassService( aeFolder ); for ( int dimID : this.get( "DimensionManager", "StorageCells", new int[0] ).getIntList() ) { this.storageCellDims.add( dimID ); DimensionManager.registerDimension( dimID, AEConfig.instance.storageProviderID ); } try { this.lastGridStorage = Long.parseLong( this.get( "Counters", "lastGridStorage", 0 ).getString() ); this.lastPlayer = this.get( "Counters", "lastPlayer", 0 ).getInt(); } catch ( NumberFormatException err ) { this.lastGridStorage = 0; this.lastPlayer = 0; } final ConfigCategory playerList = this.getCategory( "players" ); this.mappings = new PlayerMappings( playerList, AELog.INSTANCE ); } public static WorldSettings getInstance() { if ( instance == null ) { File world = DimensionManager.getCurrentSaveRootDirectory(); File aeBaseFolder = new File( world.getPath(), "AE2" ); if ( !aeBaseFolder.isDirectory() && !aeBaseFolder.mkdir() ) { throw new RuntimeException( "Failed to create " + aeBaseFolder.getAbsolutePath() ); } File compass = new File( aeBaseFolder, COMPASS_FOLDER ); if ( !compass.isDirectory() && !compass.mkdir() ) { throw new RuntimeException( "Failed to create " + compass.getAbsolutePath() ); } File spawnData = new File( aeBaseFolder, SPAWNDATA_FOLDER ); if ( !spawnData.isDirectory() && !spawnData.mkdir() ) { throw new RuntimeException( "Failed to create " + spawnData.getAbsolutePath() ); } instance = new WorldSettings( aeBaseFolder ); } return instance; } public Collection getNearByMeteorites( int dim, int chunkX, int chunkZ ) { Collection ll = new LinkedList(); synchronized ( WorldSettings.class ) { for ( int x = -1; x <= 1; x++ ) { for ( int z = -1; z <= 1; z++ ) { int cx = x + ( chunkX >> 4 ); int cz = z + ( chunkZ >> 4 ); NBTTagCompound data = this.loadSpawnData( dim, cx << 4, cz << 4 ); if ( data != null ) { // edit. int size = data.getInteger( "num" ); for ( int s = 0; s < size; s++ ) ll.add( data.getCompoundTag( String.valueOf( s ) ) ); } } } } return ll; } NBTTagCompound loadSpawnData( int dim, int chunkX, int chunkZ ) { if ( !Thread.holdsLock( WorldSettings.class ) ) throw new RuntimeException( "Invalid Request" ); NBTTagCompound data = null; File file = new File( this.spawnDataFolder, dim + '_' + ( chunkX >> 4 ) + '_' + ( chunkZ >> 4 ) + ".dat" ); if ( file.isFile() ) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream( file ); data = CompressedStreamTools.readCompressed( fileInputStream ); } catch ( Throwable e ) { data = new NBTTagCompound(); AELog.error( e ); } finally { if ( fileInputStream != null ) { try { fileInputStream.close(); } catch ( IOException e ) { AELog.error( e ); } } } } else { data = new NBTTagCompound(); } return data; } public boolean hasGenerated( int dim, int chunkX, int chunkZ ) { synchronized ( WorldSettings.class ) { NBTTagCompound data = this.loadSpawnData( dim, chunkX, chunkZ ); return data.getBoolean( chunkX + "," + chunkZ ); } } public void setGenerated( int dim, int chunkX, int chunkZ ) { synchronized ( WorldSettings.class ) { NBTTagCompound data = this.loadSpawnData( dim, chunkX, chunkZ ); // edit. data.setBoolean( chunkX + "," + chunkZ, true ); this.writeSpawnData( dim, chunkX, chunkZ, data ); } } void writeSpawnData( int dim, int chunkX, int chunkZ, NBTTagCompound data ) { if ( !Thread.holdsLock( WorldSettings.class ) ) throw new RuntimeException( "Invalid Request" ); File file = new File( this.spawnDataFolder, dim + '_' + ( chunkX >> 4 ) + '_' + ( chunkZ >> 4 ) + ".dat" ); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream( file ); CompressedStreamTools.writeCompressed( data, fileOutputStream ); } catch ( Throwable e ) { AELog.error( e ); } finally { if ( fileOutputStream != null ) { try { fileOutputStream.close(); } catch ( IOException e ) { AELog.error( e ); } } } } public boolean addNearByMeteorites( int dim, int chunkX, int chunkZ, NBTTagCompound newData ) { synchronized ( WorldSettings.class ) { NBTTagCompound data = this.loadSpawnData( dim, chunkX, chunkZ ); // edit. int size = data.getInteger( "num" ); data.setTag( String.valueOf( size ), newData ); data.setInteger( "num", size + 1 ); this.writeSpawnData( dim, chunkX, chunkZ, data ); return true; } } public void shutdown() { this.save(); for ( Integer dimID : this.storageCellDims ) DimensionManager.unregisterDimension( dimID ); this.storageCellDims.clear(); this.compass.kill(); instance = null; } @Override public void save() { // populate new data for ( GridStorageSearch gs : this.loadedStorage.keySet() ) { GridStorage thisStorage = gs.gridStorage.get(); if ( thisStorage != null && thisStorage.getGrid() != null && !thisStorage.getGrid().isEmpty() ) { String value = thisStorage.getValue(); this.get( "gridstorage", String.valueOf( thisStorage.getID() ), value ).set( value ); } } // save to files if ( this.hasChanged() ) super.save(); } public void addStorageCellDim( int newDim ) { this.storageCellDims.add( newDim ); DimensionManager.registerDimension( newDim, AEConfig.instance.storageProviderID ); NetworkHandler.instance.sendToAll( new PacketNewStorageDimension( newDim ) ); String[] values = new String[this.storageCellDims.size()]; for ( int x = 0; x < values.length; x++ ) values[x] = String.valueOf( this.storageCellDims.get( x ) ); this.get( "DimensionManager", "StorageCells", new int[0] ).set( values ); this.save(); } public CompassService getCompass() { return this.compass; } public void sendToPlayer( NetworkManager manager ) { if ( manager != null ) { for ( int newDim : this.get( "DimensionManager", "StorageCells", new int[0] ).getIntList() ) { manager.scheduleOutboundPacket( ( new PacketNewStorageDimension( newDim ) ).getProxy() ); } } else { for ( PlayerColor pc : TickHandler.INSTANCE.getPlayerColors().values() ) NetworkHandler.instance.sendToAll( pc.getPacket() ); } } public void init() { this.save(); } public WorldCoord getStoredSize( int dim ) { int x = this.get( "StorageCell" + dim, "scaleX", 0 ).getInt(); int y = this.get( "StorageCell" + dim, "scaleY", 0 ).getInt(); int z = this.get( "StorageCell" + dim, "scaleZ", 0 ).getInt(); return new WorldCoord( x, y, z ); } public void setStoredSize( int dim, int targetX, int targetY, int targetZ ) { this.get( "StorageCell" + dim, "scaleX", 0 ).set( targetX ); this.get( "StorageCell" + dim, "scaleY", 0 ).set( targetY ); this.get( "StorageCell" + dim, "scaleZ", 0 ).set( targetZ ); this.save(); } /** * lazy loading, can load any id, even ones that don't exist anymore. * * @param storageID ID of grid storage * * @return corresponding grid storage */ public GridStorage getGridStorage( long storageID ) { GridStorageSearch gss = new GridStorageSearch( storageID ); WeakReference result = this.loadedStorage.get( gss ); if ( result == null || result.get() == null ) { String id = String.valueOf( storageID ); String Data = this.get( "gridstorage", id, "" ).getString(); GridStorage thisStorage = new GridStorage( Data, storageID, gss ); gss.gridStorage = new WeakReference( thisStorage ); this.loadedStorage.put( gss, new WeakReference( gss ) ); return thisStorage; } return result.get().gridStorage.get(); } /** * create a new storage */ public GridStorage getNewGridStorage() { long storageID = this.nextGridStorage(); GridStorageSearch gss = new GridStorageSearch( storageID ); GridStorage newStorage = new GridStorage( storageID, gss ); gss.gridStorage = new WeakReference( newStorage ); this.loadedStorage.put( gss, new WeakReference( gss ) ); return newStorage; } private long nextGridStorage() { long r = this.lastGridStorage; this.lastGridStorage++; this.get( "Counters", "lastGridStorage", this.lastGridStorage ).set( Long.toString( this.lastGridStorage ) ); return r; } public void destroyGridStorage( long id ) { String stringID = String.valueOf( id ); this.getCategory( "gridstorage" ).remove( stringID ); } public int getNextOrderedValue( String name ) { Property p = this.get( "orderedValues", name, 0 ); int myValue = p.getInt(); p.set( myValue + 1 ); return myValue; } public int getPlayerID( GameProfile profile ) { ConfigCategory playerList = this.getCategory( "players" ); if ( playerList == null || profile == null || !profile.isComplete() ) return -1; String uuid = profile.getId().toString(); Property prop = playerList.get( uuid ); if ( prop != null && prop.isIntValue() ) return prop.getInt(); else { playerList.put( uuid, prop = new Property( uuid, String.valueOf( this.nextPlayer() ), Property.Type.INTEGER ) ); this.mappings.put( prop.getInt(), profile.getId() ); // add to reverse map this.save(); return prop.getInt(); } } private long nextPlayer() { long r = this.lastPlayer; this.lastPlayer++; this.get( "Counters", "lastPlayer", this.lastPlayer ).set( this.lastPlayer ); return r; } public EntityPlayer getPlayerFromID( int playerID ) { Optional maybe = this.mappings.get( playerID ); if ( maybe.isPresent() ) { final UUID uuid = maybe.get(); for ( EntityPlayer player : CommonHelper.proxy.getPlayers() ) { if ( player.getUniqueID().equals( uuid ) ) return player; } } return null; } }