Skip to content

Commit 9c36816

Browse files
committed
Attempt to limit amount of cubes sent per tick even when joining near another player. Needs more testing on multiplayer.
1 parent 091d100 commit 9c36816

File tree

2 files changed

+89
-16
lines changed

2 files changed

+89
-16
lines changed

src/main/java/io/github/opencubicchunks/cubicchunks/core/server/CubeWatcher.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,16 @@ void addPlayer(EntityPlayerMP player) {
119119
// CHECKED: 1.10.2-12.18.1.2092
120120
void removePlayer(EntityPlayerMP player) {
121121
if (!this.players.contains(player)) {
122+
if (this.players.isEmpty()) {
123+
playerCubeMap.removeEntry(this);
124+
}
122125
return;
123126
}
124127
// If we haven't loaded yet don't load the chunk just so we can clean it up
125128
if (this.cube == null) {
126129
this.players.remove(player);
127130

128131
if (this.players.isEmpty()) {
129-
if (loading) {
130-
AsyncWorldIOExecutor.dropQueuedCubeLoad(this.playerCubeMap.getWorldServer(),
131-
cubePos.getX(), cubePos.getY(), cubePos.getZ(),
132-
c -> this.cube = c);
133-
}
134-
invalid = true;
135132
playerCubeMap.removeEntry(this);
136133
}
137134
return;
@@ -146,11 +143,19 @@ void removePlayer(EntityPlayerMP player) {
146143
MinecraftForge.EVENT_BUS.post(new CubeUnWatchEvent(cube, cubePos, this, player));
147144

148145
if (this.players.isEmpty()) {
149-
invalid = true;
150146
playerCubeMap.removeEntry(this);
151147
}
152148
}
153149

150+
void invalidate() {
151+
if (loading) {
152+
AsyncWorldIOExecutor.dropQueuedCubeLoad(this.playerCubeMap.getWorldServer(),
153+
cubePos.getX(), cubePos.getY(), cubePos.getZ(),
154+
c -> this.cube = c);
155+
}
156+
invalid = true;
157+
}
158+
154159
// CHECKED: 1.10.2-12.18.1.2092
155160
boolean providePlayerCube(boolean canGenerate) {
156161
if (loading) {

src/main/java/io/github/opencubicchunks/cubicchunks/core/server/PlayerCubeMap.java

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import java.util.HashSet;
7878
import java.util.IdentityHashMap;
7979
import java.util.Iterator;
80+
import java.util.Map;
8081
import java.util.Objects;
8182
import java.util.Set;
8283
import java.util.concurrent.TimeUnit;
@@ -151,6 +152,12 @@ public class PlayerCubeMap extends PlayerChunkMap implements LightingManager.IHe
151152
*/
152153
private final Set<ColumnWatcher> columnWatchersToUpdate = new HashSet<>();
153154

155+
/**
156+
* A queue of cubes to add a player to, this limits the amount of cubes sent to a player per tick to the set limit
157+
* even when joining an area with already existing cube watchers
158+
*/
159+
private final Map<EntityPlayerMP, WatchersSortingList<CubeWatcher>> cubesToAddPlayerTo = new IdentityHashMap<>();
160+
154161
/**
155162
* Contains all CubeWatchers that need to be sent to clients,
156163
* but these cubes are not fully loaded/generated yet.
@@ -342,6 +349,7 @@ public void tick() {
342349
this.toSendToClientNeedSort = false;
343350
this.cubesToSendToClients.sort();
344351
this.columnsToSendToClients.sort();
352+
this.cubesToAddPlayerTo.forEach((p, set) -> set.sort());
345353
}
346354

347355
getWorldServer().profiler.endStartSection("generate");
@@ -413,7 +421,7 @@ public void tick() {
413421
}
414422
if (!this.cubesToSendToClients.isEmpty()) {
415423
getWorldServer().profiler.startSection("cubes");
416-
int toSend = CubicChunksConfig.cubesToSendPerTick;//sending cubes, so send 8x more at once
424+
int toSend = CubicChunksConfig.cubesToSendPerTick;
417425
Iterator<CubeWatcher> it = this.cubesToSendToClients.iterator();
418426

419427
while (it.hasNext() && toSend > 0) {
@@ -432,6 +440,29 @@ public void tick() {
432440
getWorldServer().profiler.endSection(); // cubes
433441
}
434442

443+
if (!cubesToAddPlayerTo.isEmpty()) {
444+
for (Iterator<EntityPlayerMP> iterator = cubesToAddPlayerTo.keySet().iterator(); iterator.hasNext(); ) {
445+
EntityPlayerMP entityPlayerMP = iterator.next();
446+
WatchersSortingList<CubeWatcher> watchers = cubesToAddPlayerTo.get(entityPlayerMP);
447+
int toSend = CubicChunksConfig.cubesToSendPerTick;
448+
Iterator<CubeWatcher> iter;
449+
for (iter = watchers.iterator(); toSend > 0 && iter.hasNext(); ) {
450+
CubeWatcher watcher = iter.next();
451+
watcher.addPlayer(entityPlayerMP);
452+
CubeWatcher.SendToPlayersResult state = watcher.sendToPlayers();
453+
if (state == CubeWatcher.SendToPlayersResult.WAITING_LIGHT || state == CubeWatcher.SendToPlayersResult.WAITING) {
454+
if (!cubesToGenerate.contains(watcher)) {
455+
cubesToGenerate.appendToStart(watcher);
456+
}
457+
}
458+
toSend--;
459+
iter.remove();
460+
}
461+
if (!iter.hasNext()) {
462+
iterator.remove();
463+
}
464+
}
465+
}
435466
getWorldServer().profiler.endStartSection("unload");
436467
//if there are no players - unload everything
437468
if (this.players.isEmpty()) {
@@ -581,7 +612,7 @@ public void addPlayer(EntityPlayerMP player) {
581612
}
582613
CubeWatcher cubeWatcher = getOrCreateCubeWatcher(currentPos);
583614

584-
cubeWatcher.addPlayer(player);
615+
scheduleAddPlayerToWatcher(cubeWatcher, player);
585616
});
586617
this.players.put(player.getEntityId(), playerWrapper);
587618
this.setNeedSort();
@@ -606,7 +637,7 @@ public void removePlayer(EntityPlayerMP player) {
606637
CubeWatcher watcher = getCubeWatcher(cubePos);
607638
if (watcher != null) {
608639
// remove from the watcher, it also removes the watcher if it becomes empty
609-
watcher.removePlayer(player);
640+
removePlayerFromCubeWatcher(watcher, player);
610641
}
611642

612643
// remove column watchers if needed
@@ -690,14 +721,14 @@ private void updatePlayer(PlayerWrapper entry, CubePos oldPos, CubePos newPos) {
690721
cubesToLoad.forEach(pos -> {
691722
CubeWatcher cubeWatcher = this.getOrCreateCubeWatcher(pos);
692723
assert cubeWatcher.getCubePos().equals(pos);
693-
cubeWatcher.addPlayer(entry.playerEntity);
724+
scheduleAddPlayerToWatcher(cubeWatcher, entry.playerEntity);
694725
});
695726
getWorldServer().profiler.endStartSection("removeCubes");
696727
cubesToRemove.forEach(pos -> {
697728
CubeWatcher cubeWatcher = this.getCubeWatcher(pos);
698729
if (cubeWatcher != null) {
699730
assert cubeWatcher.getCubePos().equals(pos);
700-
cubeWatcher.removePlayer(entry.playerEntity);
731+
removePlayerFromCubeWatcher(cubeWatcher, entry.playerEntity);
701732
}
702733
});
703734
getWorldServer().profiler.endStartSection("removeColumns");
@@ -710,6 +741,26 @@ private void updatePlayer(PlayerWrapper entry, CubePos oldPos, CubePos newPos) {
710741
});
711742
getWorldServer().profiler.endSection();//removeColumns
712743
getWorldServer().profiler.endSection();//updateMovedPlayer
744+
setNeedSort();
745+
}
746+
747+
private void removePlayerFromCubeWatcher(CubeWatcher cubeWatcher, EntityPlayerMP playerEntity) {
748+
if (!cubeWatcher.containsPlayer(playerEntity)) {
749+
WatchersSortingList<CubeWatcher> cubeWatchers = cubesToAddPlayerTo.get(playerEntity);
750+
if (cubeWatchers != null) {
751+
cubeWatchers.remove(cubeWatcher);
752+
}
753+
}
754+
cubeWatcher.removePlayer(playerEntity);
755+
}
756+
757+
private void scheduleAddPlayerToWatcher(CubeWatcher cubeWatcher, EntityPlayerMP playerEntity) {
758+
cubesToAddPlayerTo.computeIfAbsent(playerEntity, p -> new WatchersSortingList<>(Comparator.comparingDouble(w -> {
759+
double dx = w.getCubePos().getXCenter() - playerEntity.posX;
760+
double dy = w.getCubePos().getYCenter() - playerEntity.posY;
761+
double dz = w.getCubePos().getZCenter() - playerEntity.posZ;
762+
return dx*dx + dy*dy + dz*dz;
763+
}))).appendToEnd(cubeWatcher);
713764
}
714765

715766
// CHECKED: 1.10.2-12.18.1.2092
@@ -776,7 +827,7 @@ public final void setPlayerViewDistance(int newHorizontalViewDistance, int newVe
776827
}
777828
CubeWatcher cubeWatcher = this.getOrCreateCubeWatcher(pos);
778829
if (!cubeWatcher.containsPlayer(player)) {
779-
cubeWatcher.addPlayer(player);
830+
scheduleAddPlayerToWatcher(cubeWatcher, player);
780831
}
781832
});
782833
// either both got smaller or only one of them changed
@@ -790,10 +841,10 @@ public final void setPlayerViewDistance(int newHorizontalViewDistance, int newVe
790841

791842
cubesToUnload.forEach(pos -> {
792843
CubeWatcher cubeWatcher = this.getCubeWatcher(pos);
793-
if (cubeWatcher != null && cubeWatcher.containsPlayer(player)) {
794-
cubeWatcher.removePlayer(player);
844+
if (cubeWatcher != null) {
845+
removePlayerFromCubeWatcher(cubeWatcher, player);
795846
} else {
796-
CubicChunks.LOGGER.warn("cubeWatcher null or doesn't contain player on render distance change");
847+
CubicChunks.LOGGER.warn("cubeWatcher null on render distance change");
797848
}
798849
});
799850
columnsToUnload.forEach(pos -> {
@@ -837,6 +888,14 @@ void addToUpdateEntry(ColumnWatcher columnWatcher) {
837888

838889
// CHECKED: 1.10.2-12.18.1.2092
839890
void removeEntry(CubeWatcher cubeWatcher) {
891+
if (!cubesToAddPlayerTo.isEmpty()) {
892+
for (WatchersSortingList<CubeWatcher> value : cubesToAddPlayerTo.values()) {
893+
if (value.contains(cubeWatcher)) {
894+
return;
895+
}
896+
}
897+
}
898+
cubeWatcher.invalidate();
840899
CubePos cubePos = cubeWatcher.getCubePos();
841900
cubeWatcher.updateInhabitedTime();
842901
CubeWatcher removed = this.cubeWatchers.remove(cubePos.getX(), cubePos.getY(), cubePos.getZ());
@@ -847,6 +906,15 @@ void removeEntry(CubeWatcher cubeWatcher) {
847906
if (cubeWatcher.getCube() != null) {
848907
cubeWatcher.getCube().getTickets().remove(cubeWatcher); // remove the ticket, so this Cube can unload
849908
}
909+
if (!cubesToAddPlayerTo.isEmpty()) {
910+
for (Iterator<WatchersSortingList<CubeWatcher>> iterator = cubesToAddPlayerTo.values().iterator(); iterator.hasNext(); ) {
911+
WatchersSortingList<CubeWatcher> value = iterator.next();
912+
value.remove(cubeWatcher);
913+
if (value.isEmpty()) {
914+
iterator.remove();
915+
}
916+
}
917+
}
850918
//don't unload, ChunkGc unloads chunks
851919
}
852920

0 commit comments

Comments
 (0)