package openblocks.common; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.registry.GameRegistry; import gnu.trove.procedure.TIntProcedure; import gnu.trove.set.TIntSet; import gnu.trove.set.hash.TIntHashSet; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; import openblocks.Config; import openmods.Log; import openmods.Mods; import openmods.config.properties.ConfigurationChange; import openmods.network.event.EventDirection; import openmods.network.event.NetworkEvent; import openmods.network.event.NetworkEventMeta; import openmods.utils.ByteUtils; public class MapDataManager { public abstract static class MapIdRequest extends NetworkEvent { public List<Integer> mapIds = Lists.newArrayList(); @Override protected void readFromStream(DataInput input) { int length = ByteUtils.readVLI(input); for (int i = 0; i < length; i++) { int id = ByteUtils.readVLI(input); mapIds.add(id); } } @Override protected void writeToStream(DataOutput output) { ByteUtils.writeVLI(output, mapIds.size()); for (Integer id : mapIds) ByteUtils.writeVLI(output, id); } } @NetworkEventMeta(direction = EventDirection.C2S) public static class MapDataRequestEvent extends MapIdRequest {} @NetworkEventMeta(direction = EventDirection.S2C) public static class MapUpdatesEvent extends MapIdRequest {} @NetworkEventMeta(direction = EventDirection.S2C, compressed = true) public static class MapDataResponseEvent extends NetworkEvent { public Map<Integer, HeightMapData> maps = Maps.newHashMap(); @Override protected void readFromStream(DataInput input) throws IOException { int length = ByteUtils.readVLI(input); for (int i = 0; i < length; i++) { int id = ByteUtils.readVLI(input); HeightMapData data = new HeightMapData(id, false); data.readFromStream(input); maps.put(id, data); } } @Override protected void writeToStream(DataOutput output) throws IOException { int size = 0; for (HeightMapData data : maps.values()) if (data.isValid()) size++; ByteUtils.writeVLI(output, size); for (Map.Entry<Integer, HeightMapData> e : maps.entrySet()) { HeightMapData map = e.getValue(); if (map.isValid()) { ByteUtils.writeVLI(output, e.getKey()); map.writeToStream(output); } else Log.debug("Trying to propagate invalid map data %d", e.getKey()); } } } public final static MapDataManager instance = new MapDataManager(); private Set<Block> blockBlacklist; private Set<Integer> mapsToUpdate = Sets.newHashSet(); public static int createNewMap(World world, byte scale) { int id = world.getUniqueDataId("height_map"); HeightMapData data = new HeightMapData(id, false); data.scale = scale; data.markDirty(); world.setItemData(data.mapName, data); return id; } public static HeightMapData getMapData(World world, int mapId) { if (mapId < 0) return HeightMapData.INVALID; String name = HeightMapData.getMapName(mapId); HeightMapData result = (HeightMapData)world.loadItemData(HeightMapData.class, name); return result != null? result : HeightMapData.EMPTY; } public static void setMapData(World world, HeightMapData data) { world.setItemData(data.mapName, data); } @SubscribeEvent public void onMapDataRequest(MapDataRequestEvent evt) { World world = evt.sender.worldObj; final MapDataResponseEvent response = new MapDataResponseEvent(); final TIntSet missingMaps = new TIntHashSet(); for (Integer mapId : evt.mapIds) { final HeightMapData map = getMapData(world, mapId); if (map != null && !map.isEmpty()) response.maps.put(mapId, map); else missingMaps.add(mapId); } if (!missingMaps.isEmpty()) { boolean lessThan16 = missingMaps.forEach(new TIntProcedure() { @Override public boolean execute(int value) { return value < 16; } }); // NEI asks for items with damage 0..15 and I can't block it via API if (Config.alwaysReportInvalidMapRequests || !lessThan16 || !Loader.isModLoaded(Mods.NOTENOUGHITEMS)) Log.info("Player %s asked for non-existent maps %s", evt.sender, missingMaps.toString()); } if (!evt.mapIds.isEmpty()) evt.reply(response); } @SubscribeEvent public void onMapDataResponse(MapDataResponseEvent evt) { World world = evt.sender.worldObj; for (Map.Entry<Integer, HeightMapData> e : evt.maps.entrySet()) { HeightMapData mapData = e.getValue(); world.setItemData(mapData.mapName, mapData); } } @SubscribeEvent public void onMapUpdates(MapUpdatesEvent evt) { World world = evt.sender.worldObj; Set<Integer> mapsToUpdate = Sets.newHashSet(); for (Integer mapId : evt.mapIds) { HeightMapData map = getMapData(world, mapId); if (map != null) mapsToUpdate.add(mapId); } if (!mapsToUpdate.isEmpty()) { MapDataRequestEvent request = new MapDataRequestEvent(); request.mapIds = Lists.newArrayList(mapsToUpdate); evt.reply(request); } } public void sendUpdates(MinecraftServer server) { if (mapsToUpdate.isEmpty()) return; MapUpdatesEvent evt = new MapUpdatesEvent(); evt.mapIds.addAll(mapsToUpdate); mapsToUpdate.clear(); evt.sendToAll(); } public void markDataUpdated(World world, int mapId) { HeightMapData data = getMapData(world, mapId); data.markDirty(); mapsToUpdate.add(mapId); } public static void requestMapData(World world, int mapId) { if (world.isRemote) { MapDataRequestEvent evt = new MapDataRequestEvent(); evt.mapIds.add(mapId); evt.sendToServer(); HeightMapData stub = new HeightMapData(mapId, true); world.setItemData(stub.mapName, stub); } } private Set<Block> getBlacklist() { if (blockBlacklist == null) { blockBlacklist = Sets.newIdentityHashSet(); for (String entry : Config.mapBlacklist) { try { String[] parts = entry.split(":"); Preconditions.checkState(parts.length == 2); String modId = parts[0]; String blockName = parts[1]; Block block = GameRegistry.findBlock(modId, blockName); if (block != Blocks.air) blockBlacklist.add(block); else Log.warn("Can't find block %s", entry); } catch (Throwable t) { Log.warn(t, "Invalid entry in map blacklist: %s", entry); } } } return blockBlacklist; } @SubscribeEvent public void onReconfig(ConfigurationChange.Post evt) { if (evt.check("cartographer", "blockBlacklist")) blockBlacklist = null; } public boolean isBlockTransparent(Block block) { return getBlacklist().contains(block); } }