Cycle random stage themes every 50 jumps in the endless part and unload chunks far behind the player

This commit is contained in:
MineTec committed 2026-10-10 13:34:41 +02:00
1 parent d4465063d4
commit 05f2fcb9da
2 files changed
+51 -7

No files matched your search

@@ -5,11 +5,14 @@ import net.minestom.server.particle.Particle;
import net.minestom.server.sound.SoundEvent;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
/**
* Abschnitte entlang des Fortschritts: einmal durch das komplette Spiel, von der Wiese über
* Untergrund, Nether und Stronghold bis ins End; ab Sprung 1000 geht es endlos weiter. Jeder Abschnitt hat eigene
* Untergrund, Nether und Stronghold bis ins End; ab Sprung 1000 geht es endlos weiter, alle 50 Sprünge
* mit einem zufälligen Thema aus diesen Abschnitten. Jeder Abschnitt hat eigene
* Blöcke und eine eigene Stimmung (Tageszeit, Partikel, Musik).
*/
public class BlockPalette {
@@ -61,23 +64,48 @@ public class BlockPalette {
new Stage(850, "End", NIGHT, Particle.END_ROD, SoundEvent.MUSIC_END, List.of(
Block.END_STONE, Block.END_STONE, Block.END_STONE_BRICKS,
Block.PURPUR_BLOCK, Block.PURPUR_PILLAR, Block.OBSIDIAN
)),
// nach dem End endlos weiter mit einem neutralen Block
new Stage(1000, "Unendlich", NIGHT, null, SoundEvent.MUSIC_MENU, List.of(
Block.SMOOTH_QUARTZ
))
);
// ab hier endlos: alle ENDLESS_SEGMENT Sprünge ein zufälliges Thema aus den Abschnitten oben
private static final int ENDLESS_FROM = 1000;
private static final int ENDLESS_SEGMENT = 50;
private static final Map<Integer, Stage> endlessStages = new ConcurrentHashMap<>();
public static Stage stageAt(int index) {
if(index >= ENDLESS_FROM) return endlessStage((index - ENDLESS_FROM) / ENDLESS_SEGMENT);
for(Stage stage : STAGES.reversed()) {
if(index >= stage.from()) return stage;
}
return STAGES.getFirst();
}
/** Der letzte Abschnitt geht endlos weiter. */
public static boolean isEndless(Stage stage) {
return stage == STAGES.getLast();
return stage.from() >= ENDLESS_FROM;
}
/**
* Thema eines endlosen Abschnitts. Fest an die Abschnittsnummer gebunden (gleiches Thema beim
* Weiterspielen) und nie zweimal dasselbe hintereinander. Zwischengespeichert, damit jeder
* Abschnitt genau ein Stage-Objekt hat (Abschnittswechsel werden per Identität erkannt).
*/
private static Stage endlessStage(int segment) {
return endlessStages.computeIfAbsent(segment, n -> {
Stage theme = themeOf(n);
return new Stage(ENDLESS_FROM + n * ENDLESS_SEGMENT, "Unendlich · " + theme.name(), theme.time(), theme.ambient(), theme.music(), theme.blocks());
});
}
/** Würfelt die Themen ab Abschnitt 0 der Reihe nach, damit „nie zweimal hintereinander“ sicher gilt. */
private static Stage themeOf(int segment) {
int previous = -1;
int pick = 0;
for(int n = 0; n <= segment; n++) {
pick = new Random(n * 31L + 7).nextInt(STAGES.size());
if(pick == previous) pick = (pick + 1) % STAGES.size();
previous = pick;
}
return STAGES.get(pick);
}
public static Block getRandom(int index, Random random) {
@@ -13,6 +13,7 @@ import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.ItemEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.packet.server.play.ParticlePacket;
@@ -164,6 +165,7 @@ public class JumpGenerator {
if(reachedCheckpoint > previous) {
this.checkpoint = this.step(reachedCheckpoint).pos();
Progress.save(this.player.getUuid(), reachedCheckpoint);
this.unloadChunksBehind();
this.player.playSound(CHECKPOINT_SOUND);
this.player.showTitle(CHECKPOINT_TITLE);
this.player.sendPacket(new ParticlePacket(Particle.CRIT, this.checkpoint, this.checkpoint.add(0.5), 1.5f, 50));
@@ -200,6 +202,20 @@ public class JumpGenerator {
}
}
/**
* Die Strecke wandert immer in +z. Chunks hinter dem Checkpoint und außerhalb der Sichtweite
* werden nie wieder gebraucht (Reset und Rettung liegen davor): freigeben, sonst sammeln sich
* bei langen Läufen tausende an.
*/
private void unloadChunksBehind() {
int playerChunk = this.player.getPosition().blockZ() >> 4;
int checkpointChunk = this.checkpoint.blockZ() >> 4;
int keepFrom = Math.min(checkpointChunk, playerChunk - this.player.effectiveViewDistance()) - 1;
for(Chunk chunk : List.copyOf(this.world.getChunks())) {
if(chunk.getChunkZ() < keepFrom) this.world.unloadChunk(chunk);
}
}
private void ensureGenerated(int upTo, int stepTicks) {
int delay = 0;
while(this.generated() <= upTo) {