package com.plotsquared.sponge.util.block; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.block.BasicLocalBlockQueue; import com.plotsquared.sponge.util.SpongeUtil; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityTracker; import net.minecraft.entity.EntityTrackerEntry; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.network.play.server.SPacketChunkData; import net.minecraft.network.play.server.SPacketDestroyEntities; import net.minecraft.server.management.PlayerChunkMap; import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.IntHashMap; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.world.World; import org.spongepowered.api.world.biome.BiomeType; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashSet; public class SpongeLocalQueue extends BasicLocalBlockQueue<char[]> { public SpongeLocalQueue(String world) { super(world); } @Override public LocalChunk<char[]> getLocalChunk(int x, int z) { return new CharLocalChunk_Sponge(this, x, z) { // Custom stuff? }; } @Override public void optimize() { } public World getSpongeWorld() { return SpongeUtil.getWorld(getWorld()); } @Override public PlotBlock getBlock(int x, int y, int z) { World worldObj = getSpongeWorld(); BlockState block = worldObj.getBlock(x, y, z); return SpongeUtil.getPlotBlock(block); } @Override public void refreshChunk(int x, int z) { World world = getSpongeWorld(); Chunk nmsChunk = ((net.minecraft.world.World) world).getChunkProvider().provideChunk(x, z); if (nmsChunk == null || !nmsChunk.isLoaded()) { return; } try { ChunkPos pos = nmsChunk.getChunkCoordIntPair(); WorldServer w = (WorldServer) nmsChunk.getWorld(); PlayerChunkMap chunkMap = w.getPlayerChunkMap(); if (!chunkMap.contains(x, z)) { return; } EntityTracker tracker = w.getEntityTracker(); HashSet<EntityPlayerMP> players = new HashSet<>(); for (EntityPlayer player : w.playerEntities) { if (player instanceof EntityPlayerMP) { if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { players.add((EntityPlayerMP) player); } } } if (players.isEmpty()) { return; } HashSet<EntityTrackerEntry> entities = new HashSet<>(); ClassInheritanceMultiMap<Entity>[] entitieSlices = nmsChunk.getEntityLists(); IntHashMap<EntityTrackerEntry> entries = null; for (Field field : tracker.getClass().getDeclaredFields()) { if (field.getType() == IntHashMap.class) { field.setAccessible(true); entries = (IntHashMap<EntityTrackerEntry>) field.get(tracker); } } for (ClassInheritanceMultiMap<Entity> slice : entitieSlices) { if (slice == null) { continue; } for (Entity ent : slice) { EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null; if (entry == null) { continue; } entities.add(entry); SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId()); for (EntityPlayerMP player : players) { player.connection.sendPacket(packet); } } } // Send chunks SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535); for (EntityPlayerMP player : players) { player.connection.sendPacket(packet); } // send ents for (EntityTrackerEntry entry : entities) { try { TaskManager.IMP.taskLater(new Runnable() { @Override public void run() { for (EntityPlayerMP player : players) { if (entry.isVisibleTo(player)) { entry.removeFromTrackedPlayers(player); if (entry.getTrackedEntity() != player) { entry.updatePlayerEntity(player); } } } } }, 2); } catch (Throwable e) { e.printStackTrace(); } } } catch (Throwable e) { e.printStackTrace(); } } @Override public void fixChunkLighting(int x, int z) { Chunk nmsChunk = getChunk(getSpongeWorld(), x, z); nmsChunk.generateSkylightMap(); } public class CharLocalChunk_Sponge extends CharLocalChunk { public short[] count; public short[] air; public short[] relight; public CharLocalChunk_Sponge(BasicLocalBlockQueue parent, int x, int z) { super(parent, x, z); this.count = new short[16]; this.air = new short[16]; this.relight = new short[16]; } @Override public void setBlock(int x, int y, int z, int id, int data) { int i = MainUtil.CACHE_I[y][x][z]; int j = MainUtil.CACHE_J[y][x][z]; char[] vs = this.blocks[i]; if (vs == null) { vs = this.blocks[i] = new char[4096]; this.count[i]++; } else if (vs[j] == 0) { this.count[i]++; } switch (id) { case 0: this.air[i]++; vs[j] = (char) 1; return; case 10: case 11: case 39: case 40: case 51: case 74: case 89: case 122: case 124: case 138: case 169: this.relight[i]++; case 2: case 4: case 13: case 14: case 15: case 20: case 21: case 22: case 30: case 32: case 37: case 41: case 42: case 45: case 46: case 47: case 48: case 49: case 55: case 56: case 57: case 58: case 60: case 7: case 8: case 9: case 73: case 78: case 79: case 80: case 81: case 82: case 83: case 85: case 87: case 88: case 101: case 102: case 103: case 110: case 112: case 113: case 121: case 129: case 133: case 165: case 166: case 170: case 172: case 173: case 174: case 181: case 182: case 188: case 189: case 190: case 191: case 192: vs[j] = (char) (id << 4); return; case 130: case 76: case 62: this.relight[i]++; case 54: case 146: case 61: case 65: case 68: case 50: if (data < 2) { data = 2; } default: vs[j] = (char) ((id << 4) + data); return; } } public char[] getIdArray(int i) { return this.blocks[i]; } public int getCount(int i) { return this.count[i]; } public int getAir(int i) { return this.air[i]; } public void setCount(int i, short value) { this.count[i] = value; } public int getRelight(int i) { return this.relight[i]; } public int getTotalCount() { int total = 0; for (int i = 0; i < 16; i++) { total += this.count[i]; } return total; } public int getTotalRelight() { if (getTotalCount() == 0) { Arrays.fill(this.count, (short) 1); Arrays.fill(this.relight, Short.MAX_VALUE); return Short.MAX_VALUE; } int total = 0; for (int i = 0; i < 16; i++) { total += this.relight[i]; } return total; } } public boolean isSurrounded(char[][] sections, int x, int y, int z) { return isSolid(getId(sections, x, y + 1, z)) && isSolid(getId(sections, x + 1, y - 1, z)) && isSolid(getId(sections, x - 1, y, z)) && isSolid(getId(sections, x, y, z + 1)) && isSolid(getId(sections, x, y, z - 1)); } public boolean isSolid(int i) { return i != 0 && Block.getBlockById(i).isFullyOpaque(Block.getBlockById(i).getDefaultState()); } public int getId(char[][] sections, int x, int y, int z) { if (x < 0 || x > 15 || z < 0 || z > 15) { return 1; } if (y < 0 || y > 255) { return 1; } int i = MainUtil.CACHE_I[y][x][z]; char[] section = sections[i]; if (section == null) { return 0; } int j = MainUtil.CACHE_I[y][x][z]; int combined = section[j]; return combined >> 4; } public boolean fixLighting(CharLocalChunk_Sponge bc, Chunk nmsChunk) { try { if (!nmsChunk.isLoaded()) { return false; } ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); nmsChunk.generateSkylightMap(); net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); int X = bc.getX() << 4; int Z = bc.getZ() << 4; BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(0, 0, 0); for (int j = 0; j < sections.length; j++) { ExtendedBlockStorage section = sections[j]; if (section == null) { continue; } if ((bc.getCount(j) == 0) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { continue; } char[] array = bc.getIdArray(j); if (array != null) { int l = PseudoRandom.random.random(2); for (int k = 0; k < array.length; k++) { int i = array[k]; if (i < 16) { continue; } short id = (short) (i >> 4); switch (id) { // Lighting default: if ((k & 1) == l) { l = 1 - l; continue; } case 10: case 11: case 39: case 40: case 50: case 51: case 62: case 74: case 76: case 89: case 122: case 124: case 130: case 138: case 169: int x = MainUtil.x_loc[j][k]; int y = MainUtil.y_loc[j][k]; int z = MainUtil.z_loc[j][k]; if (isSurrounded(bc.blocks, x, y, z)) { continue; } pos.setPos(X + x, y, Z + z); nmsWorld.checkLight(pos); } } } } return true; } catch (Throwable e) { e.printStackTrace(); } return false; } @Override public final void regenChunk(int x, int z) { World worldObj = getSpongeWorld(); throw new UnsupportedOperationException("NOT SUPPORTED"); } @Override public final void setComponents(LocalChunk<char[]> lc) { setBlocks(lc); setBiomes(lc); } public Chunk getChunk(World world, int x, int z) { net.minecraft.world.chunk.Chunk chunk = ((net.minecraft.world.World) world).getChunkProvider().provideChunk(x, z); if (chunk != null && !chunk.isLoaded()) { chunk.onChunkLoad(); } return chunk; } private BlockState AIR = BlockTypes.AIR.getDefaultState(); public void setBlocks(LocalChunk<char[]> lc) { World worldObj = getSpongeWorld(); org.spongepowered.api.world.Chunk spongeChunk = (org.spongepowered.api.world.Chunk) getChunk(worldObj, lc.getX(), lc.getZ()); Chunk nmsChunk = (Chunk) spongeChunk; char[][] ids = ((CharLocalChunk) lc).blocks; for (int layer = 0; layer < 16; layer++) { char[] array = ids[layer]; if (array == null) { continue; } ExtendedBlockStorage section = nmsChunk.getBlockStorageArray()[layer]; short[] cacheX = MainUtil.x_loc[0]; short[] cacheY = MainUtil.y_loc[0]; short[] cacheZ = MainUtil.z_loc[0]; for (int j = 0; j < array.length; j++) { int combinedId = array[j]; switch (combinedId) { case 0: continue; case 1: int x = cacheX[j]; int y = cacheY[j]; int z = cacheZ[j]; section.set(x, y, z, Blocks.AIR.getDefaultState()); continue; default: int id = combinedId >> 4; Block block = Block.getBlockById(id); int data = combinedId & 0xf; IBlockState ibd; if (data != 0) { ibd = block.getStateFromMeta(data); } else { ibd = block.getDefaultState(); } x = cacheX[j]; y = cacheY[j]; z = cacheZ[j]; section.set(x, y, z, ibd); continue; } } } refreshChunk(nmsChunk.xPosition, nmsChunk.zPosition); } public void setBiomes(LocalChunk<char[]> lc) { if (lc.biomes != null) { World worldObj = getSpongeWorld(); int bx = lc.getX() << 4; int bz = lc.getX() << 4; String last = null; BiomeType biome = null; for (int x = 0; x < lc.biomes.length; x++) { String[] biomes2 = lc.biomes[x]; if (biomes2 != null) { for (int y = 0; y < biomes2.length; y++) { String biomeStr = biomes2[y]; if (biomeStr != null) { if (last == null || !StringMan.isEqual(last, biomeStr)) { biome = SpongeUtil.getBiome(biomeStr.toUpperCase()); } worldObj.setBiome(bx, 0, bz, biome); } } } } } } }