use of net.glowstone.net.message.play.game.UnloadChunkMessage in project Glowstone by GlowstoneMC.
the class GlowPlayer method streamBlocks.
/**
* Streams chunks to the player's client.
*/
private void streamBlocks() {
Set<Key> previousChunks = null;
ArrayList<Key> newChunks = new ArrayList<>();
int centralX = location.getBlockX() >> 4;
int centralZ = location.getBlockZ() >> 4;
int radius = Math.min(server.getViewDistance(), 1 + settings.getViewDistance());
if (firstStream) {
firstStream = false;
for (int x = centralX - radius; x <= centralX + radius; x++) {
for (int z = centralZ - radius; z <= centralZ + radius; z++) {
newChunks.add(GlowChunk.Key.of(x, z));
}
}
} else if (Math.abs(centralX - prevCentralX) > radius || Math.abs(centralZ - prevCentralZ) > radius) {
knownChunks.clear();
for (int x = centralX - radius; x <= centralX + radius; x++) {
for (int z = centralZ - radius; z <= centralZ + radius; z++) {
newChunks.add(GlowChunk.Key.of(x, z));
}
}
} else if (forceStream || prevCentralX != centralX || prevCentralZ != centralZ) {
previousChunks = new HashSet<>(knownChunks);
for (int x = centralX - radius; x <= centralX + radius; x++) {
for (int z = centralZ - radius; z <= centralZ + radius; z++) {
Key key = GlowChunk.Key.of(x, z);
if (knownChunks.contains(key)) {
previousChunks.remove(key);
} else {
newChunks.add(key);
}
}
}
} else {
// early end if there's no changes
return;
}
prevCentralX = centralX;
prevCentralZ = centralZ;
// sort chunks by distance from player - closer chunks sent first
newChunks.sort((a, b) -> {
double dx = 16 * a.getX() + 8 - location.getX();
double dz = 16 * a.getZ() + 8 - location.getZ();
double da = dx * dx + dz * dz;
dx = 16 * b.getX() + 8 - location.getX();
dz = 16 * b.getZ() + 8 - location.getZ();
double db = dx * dx + dz * dz;
return Double.compare(da, db);
});
// populate then send chunks to the player
// done in two steps so that all the new chunks are finalized before any of them are sent
// this prevents sending a chunk then immediately sending block changes in it because
// one of its neighbors has populated
// first step: force population then acquire lock on each chunk
newChunks.forEach(newChunk -> {
try {
world.getChunkManager().forcePopulation(newChunk.getX(), newChunk.getZ());
} catch (IllegalArgumentException e) {
// The show must go on, so catch it here!
logger.log(Level.SEVERE, "", e);
}
knownChunks.add(newChunk);
chunkLock.acquire(newChunk);
});
boolean skylight = world.getEnvironment() == Environment.NORMAL;
ByteBufAllocator alloc = session.getChannel() == null ? null : session.getChannel().alloc();
for (GlowChunk.Key key : newChunks) {
GlowChunk chunk = world.getChunk(key);
ChunkDataMessage message = chunk.toMessage(skylight, true, alloc);
if (message == null || message.getData() == null) {
// allocator failed
break;
}
session.sendAndRelease(message, message.getData());
}
// send visible block entity data
newChunks.stream().flatMap(key -> world.getChunkAt(key.getX(), key.getZ()).getRawBlockEntities().stream()).forEach(entity -> entity.update(this));
// and remove old chunks
if (previousChunks != null) {
previousChunks.forEach(key -> {
session.send(new UnloadChunkMessage(key.getX(), key.getZ()));
knownChunks.remove(key);
chunkLock.release(key);
});
previousChunks.clear();
}
}
Aggregations