Skip to content

Commit

Permalink
Memory usage optimization with chunks (#569)
Browse files Browse the repository at this point in the history
* Initial optimization w/ caching for chunk keys

* Unify Key hashCode function
  • Loading branch information
Momothereal authored and Postremus committed Oct 5, 2017
1 parent 76bf760 commit 12ed57c
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 21 deletions.
6 changes: 3 additions & 3 deletions src/main/java/net/glowstone/GlowWorld.java
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ private void updateActiveChunkCollection(GlowEntity entity) {
for (int x = cx - radius; x <= cx + radius; x++) {
for (int z = cz - radius; z <= cz + radius; z++) {
if (isChunkLoaded(cx, cz)) {
activeChunksSet.add(new Key(x, z));
activeChunksSet.add(GlowChunk.ChunkKeyStore.get(x, z));
}
}
}
Expand Down Expand Up @@ -827,7 +827,7 @@ private void prepareSpawn() {
} else {
loadChunk(x, z);
}
spawnChunkLock.acquire(new Key(x, z));
spawnChunkLock.acquire(GlowChunk.ChunkKeyStore.get(x, z));
if (System.currentTimeMillis() >= loadTime + 1000) {
int progress = 100 * current / total;
GlowServer.logger.info("Preparing spawn for " + name + ": " + progress + "%");
Expand Down Expand Up @@ -1271,7 +1271,7 @@ public boolean refreshChunk(int x, int z) {
return false;
}

Key key = new Key(x, z);
Key key = GlowChunk.ChunkKeyStore.get(x, z);
boolean result = false;

for (GlowPlayer player : getRawPlayers()) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/glowstone/block/entity/BlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import net.glowstone.block.GlowBlock;
import net.glowstone.block.GlowBlockState;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.chunk.GlowChunk.Key;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.util.nbt.CompoundTag;
Expand Down Expand Up @@ -41,7 +42,7 @@ public final Block getBlock() {
* Update this BlockEntity's visible state to all players in range.
*/
public final void updateInRange() {
Key key = new Key(block.getChunk().getX(), block.getChunk().getZ());
Key key = GlowChunk.ChunkKeyStore.get(block.getChunk().getX(), block.getChunk().getZ());
block.getWorld().getRawPlayers().stream().filter(player -> player.canSeeChunk(key)).forEach(this::update);
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/glowstone/block/itemtype/ItemPainting.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import net.glowstone.block.GlowBlock;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.chunk.GlowChunk.Key;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.entity.objects.GlowPainting;
Expand Down Expand Up @@ -37,7 +38,7 @@ public class ItemPainting extends ItemType {
)
).arrayListValues().build();

Arrays.stream(Art.values()).forEach(art -> ART_BY_SIZE.put(new Key(art.getBlockHeight(), art.getBlockWidth()), art));
Arrays.stream(Art.values()).forEach(art -> ART_BY_SIZE.put(GlowChunk.ChunkKeyStore.get(art.getBlockHeight(), art.getBlockWidth()), art));
}

@Override
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/glowstone/block/state/GlowNoteBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.glowstone.block.GlowBlock;
import net.glowstone.block.GlowBlockState;
import net.glowstone.block.entity.NoteblockEntity;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.chunk.GlowChunk.Key;
import org.bukkit.Instrument;
import org.bukkit.Location;
Expand Down Expand Up @@ -196,7 +197,7 @@ public boolean play(Instrument instrument, Note note) {

Location location = getBlock().getLocation();

Key key = new Key(getX() >> 4, getZ() >> 4);
Key key = GlowChunk.ChunkKeyStore.get(getX() >> 4, getZ() >> 4);
getWorld().getRawPlayers().stream().filter(player -> player.canSeeChunk(key)).forEach(player -> player.playNote(location, instrument, note));

return true;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/net/glowstone/chunk/ChunkManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public ChunkGenerator getGenerator() {
* @return The chunk.
*/
public GlowChunk getChunk(int x, int z) {
Key key = new Key(x, z);
Key key = GlowChunk.ChunkKeyStore.get(x, z);
if (chunks.containsKey(key)) {
return chunks.get(key);
} else {
Expand All @@ -115,7 +115,7 @@ public GlowChunk getChunk(int x, int z) {
* @return true if the chunk is loaded, otherwise false.
*/
public boolean isChunkLoaded(int x, int z) {
Key key = new Key(x, z);
Key key = GlowChunk.ChunkKeyStore.get(x, z);
return chunks.containsKey(key) && chunks.get(key).isLoaded();
}

Expand All @@ -127,7 +127,7 @@ public boolean isChunkLoaded(int x, int z) {
* @return Whether the chunk is in use.
*/
public boolean isChunkInUse(int x, int z) {
Key key = new Key(x, z);
Key key = GlowChunk.ChunkKeyStore.get(x, z);
Set<ChunkLock> lockSet = locks.get(key);
return lockSet != null && !lockSet.isEmpty();
}
Expand Down
33 changes: 30 additions & 3 deletions src/main/java/net/glowstone/chunk/GlowChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ private void initializeSection(int y, ChunkSection section) {
* @param cy the Y coordinate of the BlockEntity
* @param cz the Z coordinate of the BlockEntity
* @param type the type of BlockEntity
* @return The BlockEntity that was created.
* @return The BlockEntity that was created.
*/
public BlockEntity createEntity(int cx, int cy, int cz, int type) {
Material material = Material.getMaterial(type);
Expand Down Expand Up @@ -694,7 +694,7 @@ public ChunkDataMessage toMessage(boolean skylight) {
* Creates a new {@link ChunkDataMessage} which can be sent to a client to stream
* parts of this chunk to them.
*
* @param skylight Whether to include skylight data.
* @param skylight Whether to include skylight data.
* @param entireChunk Whether to send all chunk sections.
* @return The {@link ChunkDataMessage}.
*/
Expand Down Expand Up @@ -755,7 +755,34 @@ public static final class Key {
/**
* The coordinates.
*/
private final int x, z;
private final int x, z, hashCode;

private Key(int x, int z) {
this.x = x;
this.z = z;
this.hashCode = hashCode(x, z);
}

@Override
public int hashCode() {
return hashCode;
}

private static int hashCode(int x, int z) {
return x * 31 + z;
}
}

public static final class ChunkKeyStore {
private static final ConcurrentHashMap<Integer, Key> keys = new ConcurrentHashMap<>();

public static Key get(int x, int z) {
int id = Key.hashCode(x, z);
Key key = keys.get(id);
if (key != null) return key;
key = new Key(x, z);
keys.put(id, key);
return key;
}
}
}
8 changes: 4 additions & 4 deletions src/main/java/net/glowstone/entity/GlowPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ private void processBlockChanges() {
Map<Key, Map<BlockVector, BlockChangeMessage>> chunks = new HashMap<>();
for (BlockChangeMessage message : messages) {
if (message != null) {
Key key = new Key(message.getX() >> 4, message.getZ() >> 4);
Key key = GlowChunk.ChunkKeyStore.get(message.getX() >> 4, message.getZ() >> 4);
if (canSeeChunk(key)) {
Map<BlockVector, BlockChangeMessage> map = chunks.computeIfAbsent(key, k -> new HashMap<>());
map.put(new BlockVector(message.getX(), message.getY(), message.getZ()), message);
Expand Down Expand Up @@ -772,7 +772,7 @@ private void streamBlocks() {
int radius = Math.min(server.getViewDistance(), 1 + settings.getViewDistance());
for (int x = centralX - radius; x <= centralX + radius; x++) {
for (int z = centralZ - radius; z <= centralZ + radius; z++) {
Key key = new Key(x, z);
Key key = GlowChunk.ChunkKeyStore.get(x, z);
if (knownChunks.contains(key)) {
previousChunks.remove(key);
} else {
Expand Down Expand Up @@ -2188,7 +2188,7 @@ public void sendBlockChange(Location loc, int material, byte data) {

public void sendBlockChange(BlockChangeMessage message) {
// only send message if the chunk is within visible range
Key key = new Key(message.getX() >> 4, message.getZ() >> 4);
Key key = GlowChunk.ChunkKeyStore.get(message.getX() >> 4, message.getZ() >> 4);
if (canSeeChunk(key)) {
blockChanges.add(message);
}
Expand Down Expand Up @@ -2953,7 +2953,7 @@ private void sendBlockBreakAnimation(Location loc, int destroyStage) {
}

private void broadcastBlockBreakAnimation(GlowBlock block, int destroyStage) {
GlowChunk.Key key = new GlowChunk.Key(block.getChunk().getX(), block.getChunk().getZ());
GlowChunk.Key key = GlowChunk.ChunkKeyStore.get(block.getChunk().getX(), block.getChunk().getZ());
block.getWorld().getRawPlayers().stream()
.filter(player -> player.canSeeChunk(key) && player != this)
.forEach(player -> player.sendBlockBreakAnimation(block.getLocation(), destroyStage));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.flowpowered.network.Message;
import net.glowstone.EventFactory;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.chunk.GlowChunk.Key;
import net.glowstone.entity.GlowHangingEntity;
import net.glowstone.entity.GlowPlayer;
Expand Down Expand Up @@ -147,7 +148,7 @@ private void createTeleportMessage(BlockFace face) {
break;
}

Key key = new Key(location.getChunk().getX(), location.getChunk().getZ());
Key key = GlowChunk.ChunkKeyStore.get(location.getChunk().getX(), location.getChunk().getZ());
for (GlowPlayer player : getWorld().getRawPlayers()) {
if (player.canSeeChunk(key)) {
double x = location.getX();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import net.glowstone.GlowServer;
import net.glowstone.GlowWorld;
import net.glowstone.chunk.GlowChunk.Key;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.generator.structures.GlowStructure;
import net.glowstone.io.structure.StructureStorage;
import net.glowstone.io.structure.StructureStore;
Expand Down Expand Up @@ -36,7 +36,7 @@ public void populate(World world, Random random, Chunk source) {
if (world.getChunkAt(x, z).isLoaded() || world.getChunkAt(x, z).load(true)) {
random.setSeed(x * xRand + z * zRand ^ world.getSeed());
Map<Integer, GlowStructure> structures = ((GlowWorld) world).getStructures();
int key = new Key(x, z).hashCode();
int key = GlowChunk.ChunkKeyStore.get(x, z).hashCode();
if (!structures.containsKey(key)) {
for (StructureStore<?> store : StructureStorage.getStructureStores()) {
GlowStructure structure = store.createNewStructure((GlowWorld) world, random, x, z);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import net.glowstone.GlowServer;
import net.glowstone.GlowWorld;
import net.glowstone.chunk.GlowChunk.Key;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.generator.structures.GlowStructure;
import net.glowstone.io.StructureDataService;
import net.glowstone.io.structure.StructureStorage;
Expand Down Expand Up @@ -51,7 +51,7 @@ public Map<Integer, GlowStructure> readStructuresData() {
CompoundTag features = data.getCompound("Features");
features.getValue().keySet().stream().filter(features::isCompound).forEach(key -> {
GlowStructure structure = StructureStorage.loadStructure(world, features.getCompound(key));
structures.put(new Key(structure.getChunkX(), structure.getChunkZ()).hashCode(), structure);
structures.put(GlowChunk.ChunkKeyStore.get(structure.getChunkX(), structure.getChunkZ()).hashCode(), structure);
});
}
} else {
Expand Down

0 comments on commit 12ed57c

Please sign in to comment.