diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java index f8cfa79..6a8e3af 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -2,7 +2,9 @@ import com.google.common.collect.Sets; import com.mojang.authlib.GameProfile; + import io.netty.buffer.Unpooled; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -11,6 +13,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; + import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; @@ -95,9 +98,11 @@ import net.minecraft.world.WorldSettings; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.Chunk; + import org.apache.commons.io.Charsets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ultramine.server.chunk.ChunkSendManager; import net.minecraft.entity.item.EntityItem; import net.minecraftforge.common.ForgeHooks; @@ -233,60 +238,8 @@ this.playerNetServerHandler.sendPacket(new S13PacketDestroyEntities(aint)); } - - if (!this.loadedChunks.isEmpty()) - { - ArrayList arraylist = new ArrayList(); - Iterator iterator1 = this.loadedChunks.iterator(); - ArrayList arraylist1 = new ArrayList(); - Chunk chunk; - - while (iterator1.hasNext() && arraylist.size() < S26PacketMapChunkBulk.func_149258_c()) - { - ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair)iterator1.next(); - - if (chunkcoordintpair != null) - { - if (this.worldObj.blockExists(chunkcoordintpair.chunkXPos << 4, 0, chunkcoordintpair.chunkZPos << 4)) - { - chunk = this.worldObj.getChunkFromChunkCoords(chunkcoordintpair.chunkXPos, chunkcoordintpair.chunkZPos); - - if (chunk.func_150802_k()) - { - arraylist.add(chunk); - arraylist1.addAll(((WorldServer)this.worldObj).func_147486_a(chunkcoordintpair.chunkXPos * 16, 0, chunkcoordintpair.chunkZPos * 16, chunkcoordintpair.chunkXPos * 16 + 15, 256, chunkcoordintpair.chunkZPos * 16 + 15)); - //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it. - iterator1.remove(); - } - } - } - else - { - iterator1.remove(); - } - } - - if (!arraylist.isEmpty()) - { - this.playerNetServerHandler.sendPacket(new S26PacketMapChunkBulk(arraylist)); - Iterator iterator2 = arraylist1.iterator(); - - while (iterator2.hasNext()) - { - TileEntity tileentity = (TileEntity)iterator2.next(); - this.func_147097_b(tileentity); - } - - iterator2 = arraylist.iterator(); - - while (iterator2.hasNext()) - { - chunk = (Chunk)iterator2.next(); - this.getServerForPlayer().getEntityTracker().func_85172_a(this, chunk); - MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), this)); - } - } - } + + getChunkMgr().update(); if (this.field_143005_bX > 0L && this.mcServer.func_143007_ar() > 0 && MinecraftServer.getSystemTimeMillis() - this.field_143005_bX > (long)(this.mcServer.func_143007_ar() * 1000 * 60)) { @@ -957,7 +910,7 @@ public void func_147100_a(C15PacketClientSettings p_147100_1_) { this.translator = p_147100_1_.func_149524_c(); - int i = 256 >> p_147100_1_.func_149521_d(); + int i = /*256 >>*/ p_147100_1_.func_149521_d(); if (i > 3 && i < 15) { @@ -1010,4 +963,18 @@ { return 1.62F; } + + /* ===================================== ULTRAMINE START =====================================*/ + + private final ChunkSendManager chunkMgr = new ChunkSendManager(this); + + public ChunkSendManager getChunkMgr() + { + return chunkMgr; + } + + public int getRenderDistance() + { + return renderDistance; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java b/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java index 3f2fb51..a2ec664 100644 --- a/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java +++ b/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java @@ -2,11 +2,13 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; + import java.io.IOException; import java.util.concurrent.Semaphore; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; + import net.minecraft.network.INetHandler; import net.minecraft.network.Packet; import net.minecraft.network.PacketBuffer; @@ -28,6 +30,8 @@ private static byte[] field_149286_i = new byte[196864]; private static final String __OBFID = "CL_00001304"; private Semaphore deflateGate; + + private static final byte[] unloadSequence = new byte[] {0x78, (byte) 0x9C, 0x63, 0x64, 0x1C, (byte) 0xD9, 0x00, 0x00, (byte) 0x81, (byte) 0x80, 0x01, 0x01}; public S21PacketChunkData() {} @@ -42,10 +46,20 @@ this.field_149278_f = extracted.field_150282_a; this.deflateGate = new Semaphore(1); } + + private S21PacketChunkData(Chunk p_i45196_1_) //for unload + { + this.field_149284_a = p_i45196_1_.xPosition; + this.field_149282_b = p_i45196_1_.zPosition; + this.field_149279_g = true; + + this.field_149285_h = unloadSequence.length; + this.field_149281_e = unloadSequence; + } private void deflate() { - Deflater deflater = new Deflater(-1); + Deflater deflater = new Deflater(7); try { deflater.setInput(this.field_149278_f, 0, this.field_149278_f.length); @@ -293,4 +307,16 @@ public int field_150281_c; private static final String __OBFID = "CL_00001305"; } + + public static S21PacketChunkData makeDeflated(Chunk chunk) + { + S21PacketChunkData pkt = new S21PacketChunkData(chunk, true, 65535); + pkt.deflate(); + return pkt; + } + + public static S21PacketChunkData makeForUnload(Chunk chunk) + { + return new S21PacketChunkData(chunk); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java b/src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java index 90e5a6d..4aad780 100644 --- a/src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java +++ b/src/main/java/net/minecraft/network/play/server/S26PacketMapChunkBulk.java @@ -241,4 +241,11 @@ { this.processPacket((INetHandlerPlayClient)p_148833_1_); } + + public static S26PacketMapChunkBulk makeDeflated(List chunks) + { + S26PacketMapChunkBulk pkt = new S26PacketMapChunkBulk(chunks); + pkt.deflate(); + return pkt; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/server/management/PlayerManager.java b/src/main/java/net/minecraft/server/management/PlayerManager.java index e4afb2e..b8f3a65 100644 --- a/src/main/java/net/minecraft/server/management/PlayerManager.java +++ b/src/main/java/net/minecraft/server/management/PlayerManager.java @@ -95,7 +95,7 @@ } } - private PlayerManager.PlayerInstance getOrCreateChunkWatcher(int par1, int par2, boolean par3) + public PlayerManager.PlayerInstance getOrCreateChunkWatcher(int par1, int par2, boolean par3) { long k = (long)par1 + 2147483647L | (long)par2 + 2147483647L << 32; PlayerManager.PlayerInstance playerinstance = (PlayerManager.PlayerInstance)this.playerInstances.getValueByKey(k); @@ -124,21 +124,8 @@ public void addPlayer(EntityPlayerMP par1EntityPlayerMP) { - int i = (int)par1EntityPlayerMP.posX >> 4; - int j = (int)par1EntityPlayerMP.posZ >> 4; - par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; - par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; - - for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k) - { - for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l) - { - this.getOrCreateChunkWatcher(k, l, true).addPlayer(par1EntityPlayerMP); - } - } - - this.players.add(par1EntityPlayerMP); - this.filterChunkLoadQueue(par1EntityPlayerMP); + par1EntityPlayerMP.getChunkMgr().addTo(this); + players.add(par1EntityPlayerMP); } public void filterChunkLoadQueue(EntityPlayerMP par1EntityPlayerMP) @@ -197,22 +184,7 @@ public void removePlayer(EntityPlayerMP par1EntityPlayerMP) { - int i = (int)par1EntityPlayerMP.managedPosX >> 4; - int j = (int)par1EntityPlayerMP.managedPosZ >> 4; - - for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k) - { - for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l) - { - PlayerManager.PlayerInstance playerinstance = this.getOrCreateChunkWatcher(k, l, false); - - if (playerinstance != null) - { - playerinstance.removePlayer(par1EntityPlayerMP); - } - } - } - + par1EntityPlayerMP.getChunkMgr().removeFrom(this); this.players.remove(par1EntityPlayerMP); } @@ -225,48 +197,7 @@ public void updatePlayerPertinentChunks(EntityPlayerMP par1EntityPlayerMP) { - int i = (int)par1EntityPlayerMP.posX >> 4; - int j = (int)par1EntityPlayerMP.posZ >> 4; - double d0 = par1EntityPlayerMP.managedPosX - par1EntityPlayerMP.posX; - double d1 = par1EntityPlayerMP.managedPosZ - par1EntityPlayerMP.posZ; - double d2 = d0 * d0 + d1 * d1; - - if (d2 >= 64.0D) - { - int k = (int)par1EntityPlayerMP.managedPosX >> 4; - int l = (int)par1EntityPlayerMP.managedPosZ >> 4; - int i1 = this.playerViewRadius; - int j1 = i - k; - int k1 = j - l; - - if (j1 != 0 || k1 != 0) - { - for (int l1 = i - i1; l1 <= i + i1; ++l1) - { - for (int i2 = j - i1; i2 <= j + i1; ++i2) - { - if (!this.overlaps(l1, i2, k, l, i1)) - { - this.getOrCreateChunkWatcher(l1, i2, true).addPlayer(par1EntityPlayerMP); - } - - if (!this.overlaps(l1 - j1, i2 - k1, i, j, i1)) - { - PlayerManager.PlayerInstance playerinstance = this.getOrCreateChunkWatcher(l1 - j1, i2 - k1, false); - - if (playerinstance != null) - { - playerinstance.removePlayer(par1EntityPlayerMP); - } - } - } - } - - this.filterChunkLoadQueue(par1EntityPlayerMP); - par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; - par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; - } - } + par1EntityPlayerMP.getChunkMgr().updatePlayerPertinentChunks(); } public boolean isPlayerWatchingChunk(EntityPlayerMP par1EntityPlayerMP, int par2, int par3) @@ -280,7 +211,7 @@ return par0 * 16 - 16; } - class PlayerInstance + public class PlayerInstance { private final List playersWatchingChunk = new ArrayList(); private final ChunkCoordIntPair chunkLocation; @@ -289,25 +220,11 @@ private int flagsYAreasToUpdate; private long previousWorldTime; private static final String __OBFID = "CL_00001435"; - - private boolean loaded = false; - private Runnable loadedRunnable = new Runnable() - { - public void run() - { - PlayerInstance.this.loaded = true; - for(Object o : playersWatchingChunk) - { - EntityPlayerMP p = (EntityPlayerMP)o; - p.loadedChunks.add(chunkLocation); - } - } - }; public PlayerInstance(int par2, int par3) { this.chunkLocation = new ChunkCoordIntPair(par2, par3); - getWorldServer().theChunkProviderServer.loadAsync(par2, par3, this.loadedRunnable); +// getWorldServer().theChunkProviderServer.loadAsync(par2, par3, this.loadedRunnable); } public void addPlayer(EntityPlayerMP par1EntityPlayerMP) @@ -324,10 +241,6 @@ } this.playersWatchingChunk.add(par1EntityPlayerMP); - if(loaded) - { - par1EntityPlayerMP.loadedChunks.add(this.chunkLocation); - } } } @@ -335,29 +248,15 @@ { if (this.playersWatchingChunk.contains(par1EntityPlayerMP)) { - - if (!this.loaded) - { - this.playersWatchingChunk.remove(par1EntityPlayerMP); - if(this.playersWatchingChunk.isEmpty()) - { - ChunkIOExecutor.dropQueuedChunkLoad(getWorldServer(), this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos, this.loadedRunnable); - long i = (long) this.chunkLocation.chunkXPos + 2147483647L | (long) this.chunkLocation.chunkZPos + 2147483647L << 32; - playerInstances.remove(i); - chunkWatcherWithPlayers.remove(this); - } - return; - } - Chunk chunk = PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos); - if (chunk.func_150802_k()) +// if (chunk.func_150802_k()) { - par1EntityPlayerMP.playerNetServerHandler.sendPacket(new S21PacketChunkData(chunk, true, 0)); + par1EntityPlayerMP.playerNetServerHandler.sendPacket(S21PacketChunkData.makeForUnload(chunk)); } this.playersWatchingChunk.remove(par1EntityPlayerMP); - par1EntityPlayerMP.loadedChunks.remove(this.chunkLocation); +// par1EntityPlayerMP.loadedChunks.remove(this.chunkLocation); MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.UnWatch(chunkLocation, par1EntityPlayerMP)); diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java index dd3db56..7548fc1 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -253,7 +253,7 @@ int cx = MathHelper.floor_double(par1EntityPlayerMP.posX) >> 4; - int cz = MathHelper.floor_double(par1EntityPlayerMP.posX) >> 4; + int cz = MathHelper.floor_double(par1EntityPlayerMP.posZ) >> 4; if(worldserver.chunkExists(cx, cz)) { worldserver.spawnEntityInWorld(par1EntityPlayerMP); diff --git a/src/main/java/net/minecraft/world/SpawnerAnimals.java b/src/main/java/net/minecraft/world/SpawnerAnimals.java index e933b55..890af08 100644 --- a/src/main/java/net/minecraft/world/SpawnerAnimals.java +++ b/src/main/java/net/minecraft/world/SpawnerAnimals.java @@ -216,6 +216,8 @@ public static boolean canCreatureTypeSpawnAtLocation(EnumCreatureType par0EnumCreatureType, World par1World, int par2, int par3, int par4) { + if(!par1World.blockExists(par2, par3, par4)) return false; + if (par0EnumCreatureType.getCreatureMaterial() == Material.water) { return par1World.getBlock(par2, par3, par4).getMaterial().isLiquid() && par1World.getBlock(par2, par3 - 1, par4).getMaterial().isLiquid() && !par1World.getBlock(par2, par3 + 1, par4).isNormalCube(); diff --git a/src/main/java/net/minecraft/world/World.java b/src/main/java/net/minecraft/world/World.java index 367caae..3c3bb5d 100644 --- a/src/main/java/net/minecraft/world/World.java +++ b/src/main/java/net/minecraft/world/World.java @@ -2739,15 +2739,16 @@ k = MathHelper.floor_double(entityplayer.posZ / 16.0D); int b0 = ConfigurationHandler.getServerConfig().chunkUpdateRadius; + activeChunkSet.put(ChunkHash.chunkToKey(j, k), (byte)0); for (int l = -b0; l <= b0; ++l) { for (int i1 = -b0; i1 <= b0; ++i1) { int cx = l + j; - int cy = i1 + k; - if(chunkExists(cx, cy)) + int cz = i1 + k; + if(chunkExists(cx, cz) && chunkExists(cx-1, cz) && chunkExists(cx, cz-1) && chunkExists(cx+1, cz) && chunkExists(cx, cz+1)) { - int key = ChunkHash.chunkToKey(cx, cy); + int key = ChunkHash.chunkToKey(cx, cz); int priority = Math.max(Math.abs(l), Math.abs(i1)); //Chunk chunk = this.chunkProvider.provideChunk(cx, cy); //if(priority > 1) priority -= Math.min(priority-2, (int)(this.getTotalWorldTime() - chunk.lastActiveOrBindTick)/20); diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index 0fad5a3..9a5fe91 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -378,4 +378,9 @@ ChunkIOExecutor.queueChunkLoad(this.worldObj, (AnvilChunkLoader)currentChunkLoader, this, x, z, callback); } } + + public Chunk getChunkIfExists(int cx, int cz) + { + return loadedChunkHashMap.get(ChunkHash.chunkToKey(cx, cz)); + } } \ No newline at end of file diff --git a/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java b/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java new file mode 100644 index 0000000..6dea730 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/ChunkSendManager.java @@ -0,0 +1,339 @@ +package org.ultramine.server.chunk; + +import gnu.trove.TCollections; +import gnu.trove.iterator.TIntIterator; +import gnu.trove.set.TIntSet; +import gnu.trove.set.hash.TIntHashSet; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import org.ultramine.server.ConfigurationHandler; +import org.ultramine.server.util.BlockFace; +import org.ultramine.server.util.ChunkCoordComparator; +import org.ultramine.server.util.TIntArrayListImpl; + +import com.google.common.collect.Queues; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.Packet; +import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.server.management.PlayerManager; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.MathHelper; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkWatchEvent; + +public class ChunkSendManager +{ + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final int MAX_QUEUE_SIZE = 4; + private static final int DEFAULT_RATE = 5; + private static final double MIN_RATE = 0.2d; + private static final double MAX_RATE = 16d; + + private final EntityPlayerMP player; + private PlayerManager manager; + private BlockFace lastFace; + + private final TIntArrayListImpl toSend = new TIntArrayListImpl((int)Math.pow(ConfigurationHandler.getServerConfig().vanilla.viewDistance, 2)); + private final TIntSet sending = TCollections.synchronizedSet(new TIntHashSet()); + private final TIntSet sended = new TIntHashSet(); + private final Queue toUpdate = Queues.newConcurrentLinkedQueue(); + private final Queue toUnload = Queues.newConcurrentLinkedQueue(); + private final AtomicInteger sendingQueueSize = new AtomicInteger(); + + private int lastQueueSize; + private double rate = DEFAULT_RATE; + private int intervalCounter = 1; + + public ChunkSendManager(EntityPlayerMP player) + { + this.player = player; + } + + private void sortSendQueue() + { + int cx = MathHelper.floor_double(player.posX) >> 4; + int cz = MathHelper.floor_double(player.posZ) >> 4; + toSend.sort(ChunkCoordComparator.get(lastFace = BlockFace.yawToFace(player.rotationYaw), cx, cz)); + } + + public void addTo(PlayerManager manager) + { + if(this.manager != null) throw new IllegalStateException("PlayerManager already set"); + this.manager = manager; + + player.managedPosX = player.posX; + player.managedPosZ = player.posZ; + + int cx = MathHelper.floor_double(player.posX) >> 4; + int cz = MathHelper.floor_double(player.posZ) >> 4; + int viewRadius = ConfigurationHandler.getServerConfig().vanilla.viewDistance; + + for (int x = cx - viewRadius; x <= cx + viewRadius; ++x) + { + for (int z = cz - viewRadius; z <= cz + viewRadius; ++z) + { + toSend.add(ChunkHash.chunkToKey(x, z)); + } + } + + sortSendQueue(); + + sendChunks(Math.max(1, (int)rate)); + } + + public void removeFrom(PlayerManager manager) + { + if(this.manager != manager) throw new IllegalStateException(); + + toSend.clear(); + sending.clear(); + + for(TIntIterator it = sended.iterator(); it.hasNext();) + { + int key = it.next(); + PlayerManager.PlayerInstance pi = manager.getOrCreateChunkWatcher(ChunkHash.keyToX(key), ChunkHash.keyToZ(key), false); + if (pi != null) pi.removePlayer(player); + } + + sended.clear(); + this.manager = null; + } + + public void update() + { + if(!toSend.isEmpty()) + { + int queueSize = sendingQueueSize.get(); + + if(queueSize == 0) + { + rate += 0.14; + } + else if(queueSize > lastQueueSize) + { + if(queueSize > MAX_QUEUE_SIZE) + rate -= 0.14; + else + rate -= 0.07; + } + if(rate < MIN_RATE) rate = MIN_RATE; + else if(rate > MAX_RATE) rate = MAX_RATE; + + if(queueSize == 0 || (queueSize != lastQueueSize && queueSize <= MAX_QUEUE_SIZE)) + { + lastQueueSize = queueSize; + + if(rate >= 1) + { + sendChunks((int)rate); + } + else + { + int interval = Math.max(1, (int)(1/rate)); + if(intervalCounter++ >= interval) + { + intervalCounter = 1; + sendChunks(1); + } + } + + } + } + + while(!toUpdate.isEmpty()) + { + Chunk chunk = toUpdate.poll(); + int key = ChunkHash.chunkToKey(chunk.xPosition, chunk.zPosition); + + if(sending.contains(key)) + { + manager.getOrCreateChunkWatcher(chunk.xPosition, chunk.zPosition, true).addPlayer(player); + + List tes = manager.getWorldServer().func_147486_a(chunk.xPosition * 16, 0, chunk.zPosition * 16, chunk.xPosition * 16 + 15, 256, chunk.zPosition * 16 + 15); + for(Object o : tes) + { + TileEntity te = (TileEntity)o; + Packet packet = te.getDescriptionPacket(); + + if (packet != null) + { + player.playerNetServerHandler.sendPacket(packet); + } + } + + manager.getWorldServer().getEntityTracker().func_85172_a(player, chunk); + MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), player)); + + sended.add(key); + sending.remove(key); + } + else + { + player.playerNetServerHandler.sendPacket(S21PacketChunkData.makeForUnload(chunk)); + + PlayerManager.PlayerInstance pi = manager.getOrCreateChunkWatcher(chunk.xPosition, chunk.zPosition, false); + if (pi == null) + ((WorldServer)chunk.worldObj).theChunkProviderServer.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); + } + } + + while(!toUnload.isEmpty()) + { + Chunk chunk = toUnload.poll(); + + PlayerManager.PlayerInstance pi = manager.getOrCreateChunkWatcher(chunk.xPosition, chunk.zPosition, false); + if (pi == null) + ((WorldServer)chunk.worldObj).theChunkProviderServer.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); + } + } + + private void sendChunks(int count) + { + for(int i = 0, s = Math.min(count, toSend.size()); i < s; i++) + { + int key = toSend.removeAt(0); + sending.add(key); + sendingQueueSize.incrementAndGet(); + int ncx = ChunkHash.keyToX(key); + int ncz = ChunkHash.keyToZ(key); + manager.getWorldServer().theChunkProviderServer.loadAsync(ncx, ncz, new ChunkLoadCallback(ncx, ncz)); + } + } + + public void updatePlayerPertinentChunks() + { + int cx = MathHelper.floor_double(player.posX) >> 4; + int cz = MathHelper.floor_double(player.posZ) >> 4; + double d0 = player.managedPosX - player.posX; + double d1 = player.managedPosZ - player.posZ; + double square = d0 * d0 + d1 * d1; + + boolean sorted = false; + + if (square >= 64.0D) + { + int lastX = MathHelper.floor_double(player.managedPosX) >> 4; + int lastZ = MathHelper.floor_double(player.managedPosZ) >> 4; + int view = ConfigurationHandler.getServerConfig().vanilla.viewDistance; + int movX = cx - lastX; + int movZ = cz - lastZ; + + if (movX != 0 || movZ != 0) + { + for (int x = cx - view; x <= cx + view; ++x) + { + for (int z = cz - view; z <= cz + view; ++z) + { + if (!overlaps(x, z, lastX, lastZ, view)) + { + toSend.add(ChunkHash.chunkToKey(x, z)); + } + + if (!overlaps(x - movX, z - movZ, cx, cz, view)) + { + int key = ChunkHash.chunkToKey(x - movX, z - movZ); + if(!toSend.remove(key)) + { + if(sended.contains(key)) + { + PlayerManager.PlayerInstance pi = manager.getOrCreateChunkWatcher(x - movX, z - movZ, false); + if(pi != null) pi.removePlayer(player); + sended.remove(key); + } + else + { + sending.remove(key); + } + } + } + } + } + + sortSendQueue(); + sorted = true; + player.managedPosX = player.posX; + player.managedPosZ = player.posZ; + } + } + + if(!sorted) + { + BlockFace face = BlockFace.yawToFace(player.rotationYaw); + if(face != lastFace) + { + sortSendQueue(); + } + } + } + + private boolean overlaps(int x, int z, int lastX, int lastZ, int radius) + { + int movX = x - lastX; + int movZ = z - lastZ; + return movX >= -radius && movX <= radius ? movZ >= -radius && movZ <= radius : false; + } + + + + + private class ChunkLoadCallback implements Runnable + { + private final int x; + private final int z; + + public ChunkLoadCallback(int x, int z) + { + this.x = x; + this.z = z; + } + + @Override + public void run() + { + executor.execute(new CompressAndSendChunkTask(player.worldObj.getChunkFromChunkCoords(x, z))); + } + } + + private class CompressAndSendChunkTask implements Runnable + { + private final Chunk chunk; + + public CompressAndSendChunkTask(Chunk chunk) + { + this.chunk = chunk; + } + + @Override + public void run() + { + if(sending.contains(ChunkHash.chunkToKey(chunk.xPosition, chunk.zPosition))) + { + player.playerNetServerHandler.netManager.scheduleOutboundPacket(S21PacketChunkData.makeDeflated(chunk), + new GenericFutureListener>() + { + @Override + public void operationComplete(Future future) throws Exception + { + sendingQueueSize.decrementAndGet(); + } + }); + + toUpdate.add(chunk); + } + else + { + toUnload.add(chunk); + } + } + } +} diff --git a/src/main/java/org/ultramine/server/util/BlockFace.java b/src/main/java/org/ultramine/server/util/BlockFace.java new file mode 100644 index 0000000..0228b35 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/BlockFace.java @@ -0,0 +1,173 @@ +package org.ultramine.server.util; + +import gnu.trove.map.TObjectByteMap; +import gnu.trove.map.hash.TObjectByteHashMap; + +public enum BlockFace +{ + NORTH(0, 0, -1), + EAST(1, 0, 0), + SOUTH(0, 0, 1), + WEST(-1, 0, 0), + UP(0, 1, 0), + DOWN(0, -1, 0), + NORTH_EAST(NORTH, EAST), + NORTH_WEST(NORTH, WEST), + SOUTH_EAST(SOUTH, EAST), + SOUTH_WEST(SOUTH, WEST), + WEST_NORTH_WEST(WEST, NORTH_WEST), + NORTH_NORTH_WEST(NORTH, NORTH_WEST), + NORTH_NORTH_EAST(NORTH, NORTH_EAST), + EAST_NORTH_EAST(EAST, NORTH_EAST), + EAST_SOUTH_EAST(EAST, SOUTH_EAST), + SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST), + SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST), + WEST_SOUTH_WEST(WEST, SOUTH_WEST), + SELF(0, 0, 0); + + private final int modX; + private final int modY; + private final int modZ; + + private BlockFace(final int modX, final int modY, final int modZ) + { + this.modX = modX; + this.modY = modY; + this.modZ = modZ; + } + + private BlockFace(final BlockFace face1, final BlockFace face2) + { + this.modX = face1.getModX() + face2.getModX(); + this.modY = face1.getModY() + face2.getModY(); + this.modZ = face1.getModZ() + face2.getModZ(); + } + + public int getModX() + { + return modX; + } + + public int getModY() + { + return modY; + } + + public int getModZ() + { + return modZ; + } + + public BlockFace getOppositeFace() + { + switch(this) + { + case NORTH: + return BlockFace.SOUTH; + + case SOUTH: + return BlockFace.NORTH; + + case EAST: + return BlockFace.WEST; + + case WEST: + return BlockFace.EAST; + + case UP: + return BlockFace.DOWN; + + case DOWN: + return BlockFace.UP; + + case NORTH_EAST: + return BlockFace.SOUTH_WEST; + + case NORTH_WEST: + return BlockFace.SOUTH_EAST; + + case SOUTH_EAST: + return BlockFace.NORTH_WEST; + + case SOUTH_WEST: + return BlockFace.NORTH_EAST; + + case WEST_NORTH_WEST: + return BlockFace.EAST_SOUTH_EAST; + + case NORTH_NORTH_WEST: + return BlockFace.SOUTH_SOUTH_EAST; + + case NORTH_NORTH_EAST: + return BlockFace.SOUTH_SOUTH_WEST; + + case EAST_NORTH_EAST: + return BlockFace.WEST_SOUTH_WEST; + + case EAST_SOUTH_EAST: + return BlockFace.WEST_NORTH_WEST; + + case SOUTH_SOUTH_EAST: + return BlockFace.NORTH_NORTH_WEST; + + case SOUTH_SOUTH_WEST: + return BlockFace.NORTH_NORTH_EAST; + + case WEST_SOUTH_WEST: + return BlockFace.EAST_NORTH_EAST; + + case SELF: + return BlockFace.SELF; + } + + return BlockFace.SELF; + } + + public BlockFace rotate(int notchCount) + { + return notchToFace(faceToNotch(this) + notchCount); + } + + private static final BlockFace[] AXIS = new BlockFace[4]; + private static final BlockFace[] RADIAL = {SOUTH, SOUTH_WEST, WEST, NORTH_WEST, NORTH, NORTH_EAST, EAST, SOUTH_EAST}; + private static final TObjectByteMap NOTCHES = new TObjectByteHashMap(); + + static + { + for (byte i = 0; i < RADIAL.length; i++) + { + NOTCHES.put(RADIAL[i], i); + } + for (int i = 0; i < AXIS.length; i++) + { + AXIS[i] = RADIAL[i << 1]; + } + } + + public static BlockFace notchToFace(int notch) + { + return RADIAL[notch & 0x7]; + } + + public static int faceToNotch(BlockFace face) + { + return NOTCHES.get(face); + } + + public static BlockFace yawToFace(float yaw) + { + return yawToFace(yaw, true); + } + + public static BlockFace yawToFace(float yaw, boolean useSubCardinalDirections) + { + if (useSubCardinalDirections) + { + return RADIAL[Math.round(yaw / 45f) & 0x7]; + } + else + { + return AXIS[Math.round(yaw / 90f) & 0x3]; + } + } +} diff --git a/src/main/java/org/ultramine/server/util/ChunkCoordComparator.java b/src/main/java/org/ultramine/server/util/ChunkCoordComparator.java new file mode 100644 index 0000000..781c177 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/ChunkCoordComparator.java @@ -0,0 +1,249 @@ +package org.ultramine.server.util; + +import org.ultramine.server.chunk.ChunkHash; + +public class ChunkCoordComparator implements IntComparator +{ + private static final int VIEW = 15; + private static final int VIEWWIDTH = VIEW*2+1; + + private static final ChunkCoordComparator[] comparators = new ChunkCoordComparator[8]; + + static{init();} + public static void init(/*ChunkSendMode mode*/) + { + try + { + for(int i = 0; i < 8; i++) + { + comparators[i] = new ChunkCoordComparator(BlockFace.notchToFace(i)); +// switch(mode) +// { +// case SLOPE: + comparators[i].generateSlope(); +// break; +// default: +// comparators[i].generateSpiral(); +// break; +// } + } + } + catch(Throwable t) + { + t.printStackTrace(); + } + } + + public static ChunkCoordComparator get(BlockFace direction, int middleX, int middleZ) + { + return new ChunkCoordComparator(comparators[BlockFace.faceToNotch(direction)], middleX, middleZ); + } + + private int index = 0; + private final BlockFace direction; + private final int[][] indices; + private final int middleX; + private final int middleZ; + + private ChunkCoordComparator(ChunkCoordComparator source, int middleX, int middleZ) + { + this.indices = source.indices; + this.direction = source.direction; + this.middleX = middleX; + this.middleZ = middleZ; + } + + private ChunkCoordComparator(BlockFace direction) + { + this.direction = direction; + this.indices = new int[VIEWWIDTH][VIEWWIDTH]; + middleX = 0; + middleZ = 0; + } + + private void generate(int dx, int dz) + { + dx += VIEW; + dz += VIEW; + if(dx >= 0 && dx < this.indices.length) + { + int[] dzaint = this.indices[dx]; + if(dz >= 0 && dz < dzaint.length) + { + if(dzaint[dz] == 0) + { + dzaint[dz] = this.index++; + } + } + } + } + + private void generateLayer(final int layer, final double factor) + { + int count = (int) (layer * factor) + 1; + // get modifiers from direction + MoveMod[] mods = MoveMod.get(direction); + // Get the chunk to start at + int startx = this.direction.getModX() * layer; + int startz = this.direction.getModZ() * layer; + // Send starter chunk + this.generate(startx, startz); + // Peel + int x1 = startx; + int z1 = startz; + int x2 = startx; + int z2 = startz; + while(--count > 0) + { + // offset the chunks + x1 += mods[0].direction.getModX(); + z1 += mods[0].direction.getModZ(); + x2 += mods[1].direction.getModX(); + z2 += mods[1].direction.getModZ(); + // mod update + mods[0].next(x1, z1, layer); + mods[1].next(x2, z2, layer); + // got till the end? + this.generate(x1, z1); + if(x1 == x2 && z1 == z2) + { + return; + } + else + { + this.generate(x2, z2); + } + } + } + + private void generateSpiral() + { + // main chunk + this.generate(0, 0); + // Only full layers + for(int layer = 1; layer <= VIEW; layer++) + { + this.generateLayer(layer, 4); + } + } + + private void generateSlope() + { + // main chunk + this.generate(0, 0); + + // to this layer full layers are sent, after half + final int threshold1 = 2; + // at this layer less than half are sent + final int threshold2 = 5; + + for(int layer = 1; layer <= VIEW; layer++) + { + if(layer <= threshold1) + { + this.generateLayer(layer, 4); + } + else if(layer <= threshold2) + { + this.generateLayer(layer, 2); + } + else + { + this.generateLayer(layer, 1.5); + } + } + + // end with only full layers + for(int layer = 1; layer <= VIEW; layer++) + { + this.generateLayer(layer, 4); + } + } + + private static class MoveMod + { + private MoveMod(BlockFace direction, boolean right) + { + this.direction = direction; + this.right = right; + } + + public BlockFace direction; + public boolean right; + + public void next(int dx, int dz, int limit) + { + if(Math.abs(dx) >= limit && Math.abs(dz) >= limit) + { + this.direction = direction.rotate(right ? 2 : -2); + } + } + + public static MoveMod[] get(BlockFace direction) + { + MoveMod[] mods = new MoveMod[2]; + if(direction == BlockFace.NORTH) + { + mods[0] = new MoveMod(BlockFace.WEST, false); + mods[1] = new MoveMod(BlockFace.EAST, true); + } + else if(direction == BlockFace.SOUTH) + { + mods[0] = new MoveMod(BlockFace.WEST, true); + mods[1] = new MoveMod(BlockFace.EAST, false); + } + else if(direction == BlockFace.EAST) + { + mods[0] = new MoveMod(BlockFace.NORTH, false); + mods[1] = new MoveMod(BlockFace.SOUTH, true); + } + else if(direction == BlockFace.WEST) + { + mods[0] = new MoveMod(BlockFace.NORTH, true); + mods[1] = new MoveMod(BlockFace.SOUTH, false); + } + else if(direction == BlockFace.NORTH_EAST) + { + mods[0] = new MoveMod(BlockFace.WEST, false); + mods[1] = new MoveMod(BlockFace.SOUTH, true); + } + else if(direction == BlockFace.SOUTH_EAST) + { + mods[0] = new MoveMod(BlockFace.WEST, true); + mods[1] = new MoveMod(BlockFace.NORTH, false); + } + else if(direction == BlockFace.SOUTH_WEST) + { + mods[0] = new MoveMod(BlockFace.NORTH, true); + mods[1] = new MoveMod(BlockFace.EAST, false); + } + else if(direction == BlockFace.NORTH_WEST) + { + mods[0] = new MoveMod(BlockFace.SOUTH, false); + mods[1] = new MoveMod(BlockFace.EAST, true); + } + return mods; + } + } + + public int getIndex(int x, int z) + { + x -= middleX; + z -= middleZ; + if(Math.abs(x) > VIEW || Math.abs(z) > VIEW) + { + return Integer.MAX_VALUE; + } + return this.indices[x + VIEW][z + VIEW]; + } + + @Override + public int compare(int coord1, int coord2) + { + if(coord1 == coord2) + { + return 0; + } + return getIndex(ChunkHash.keyToX(coord1), ChunkHash.keyToZ(coord1)) - getIndex(ChunkHash.keyToX(coord2), ChunkHash.keyToZ(coord2)); + } +} diff --git a/src/main/java/org/ultramine/server/util/CollectionUtil.java b/src/main/java/org/ultramine/server/util/CollectionUtil.java new file mode 100644 index 0000000..f907b27 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/CollectionUtil.java @@ -0,0 +1,65 @@ +package org.ultramine.server.util; + +public class CollectionUtil +{ + private static final int INSERTIONSORT_THRESHOLD = 7; + + public static void sort(int[] a, IntComparator c) + { + int[] aux = a.clone(); + mergeSort(aux, a, 0, a.length, 0, c); + } + + public static void sort(int[] a, int start, int length, IntComparator c) + { + int[] aux = a.clone(); + mergeSort(aux, a, start, length, 0, c); + } + + private static void mergeSort(int[] src, int[] dest, int low, int high, int off, IntComparator c) + { + int length = high - low; + + // Insertion sort on smallest arrays + if(length < INSERTIONSORT_THRESHOLD) + { + for(int i = low; i < high; i++) + for(int j = i; j > low && c.compare(dest[j - 1], dest[j]) > 0; j--) + swap(dest, j, j - 1); + return; + } + + // Recursively sort halves of dest into src + int destLow = low; + int destHigh = high; + low += off; + high += off; + int mid = (low + high) >>> 1; + mergeSort(dest, src, low, mid, -off, c); + mergeSort(dest, src, mid, high, -off, c); + + // If list is already sorted, just copy from src to dest. This is an + // optimization that results in faster sorts for nearly ordered lists. + if(c.compare(src[mid - 1], src[mid]) <= 0) + { + System.arraycopy(src, low, dest, destLow, length); + return; + } + + // Merge sorted halves (now in src) into dest + for(int i = destLow, p = low, q = mid; i < destHigh; i++) + { + if(q >= high || p < mid && c.compare(src[p], src[q]) <= 0) + dest[i] = src[p++]; + else + dest[i] = src[q++]; + } + } + + private static void swap(int[] x, int a, int b) + { + int t = x[a]; + x[a] = x[b]; + x[b] = t; + } +} diff --git a/src/main/java/org/ultramine/server/util/IntComparator.java b/src/main/java/org/ultramine/server/util/IntComparator.java new file mode 100644 index 0000000..5b27180 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/IntComparator.java @@ -0,0 +1,6 @@ +package org.ultramine.server.util; + +public interface IntComparator +{ + int compare(int i1, int i2); +} diff --git a/src/main/java/org/ultramine/server/util/TIntArrayListImpl.java b/src/main/java/org/ultramine/server/util/TIntArrayListImpl.java new file mode 100644 index 0000000..2f9f6d5 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/TIntArrayListImpl.java @@ -0,0 +1,43 @@ +package org.ultramine.server.util; + +import gnu.trove.TIntCollection; +import gnu.trove.list.array.TIntArrayList; + +public class TIntArrayListImpl extends TIntArrayList +{ + + public TIntArrayListImpl() + { + super(); + } + + public TIntArrayListImpl(int capacity, int no_entry_value) + { + super(capacity, no_entry_value); + } + + public TIntArrayListImpl(int capacity) + { + super(capacity); + } + + public TIntArrayListImpl(int[] values, int no_entry_value, boolean wrap) + { + super(values, no_entry_value, wrap); + } + + public TIntArrayListImpl(int[] values) + { + super(values); + } + + public TIntArrayListImpl(TIntCollection collection) + { + super(collection); + } + + public void sort(IntComparator comp) + { + CollectionUtil.sort(_data, 0, _pos, comp); + } +}