Skip to content

Commit

Permalink
refactor: refactored item spawner logic to a single sync task instead…
Browse files Browse the repository at this point in the history
… of two independent tasks which often desync, ported features from 0.2.30

Ported:
062a3c0 fix: spawner desync in SBA and wrong total spawned items computation
41bd314 fix: attempt to fix empty spawners

Not ported yet:
cd98595 feat: /bw cheatIn command
  • Loading branch information
Misat11 committed Nov 19, 2023
1 parent 3e747f9 commit b719f07
Showing 1 changed file with 122 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,24 @@ public class ItemSpawnerImpl implements ItemSpawner, SerializableGameComponent {
private boolean spawnerIsFullHologram = false;
private boolean rerenderHologram = false;
private double currentLevelOnHologram = -1;
private GameImpl game;
private Task spawningTask;
private Task hologramTask;
private boolean started;
private boolean disabled;
private boolean certainPopularServerHolo;
private volatile boolean firstTick = true;
@Getter
private Pair<Long, TaskerTime> currentInterval;
private long elapsedTime;
private long remainingTimeToSpawn;
private long countdownDelay;
private volatile long currentCycle;
private boolean spawnerLockedFull;
private Task runningTask;

public ItemSpawnerImpl(Location location, ItemSpawnerTypeImpl itemSpawnerType) {
this.location = location;
this.itemSpawnerType = itemSpawnerType;
}

public int nextMaxSpawn(int calculated) {
private int nextMaxSpawn(int calculated) {
if (amountPerSpawn <= 0) {
if (hologram != null && (!spawnerIsFullHologram || currentLevelOnHologram != amountPerSpawn)) {
spawnerIsFullHologram = true;
Expand All @@ -147,6 +147,7 @@ public int nextMaxSpawn(int calculated) {
int spawned = getSpawnedItemsCount();

if (spawned >= maxSpawnedResources) {
spawnerLockedFull = true;
if (hologram != null && !spawnerIsFullHologram) {
spawnerIsFullHologram = true;
if (certainPopularServerHolo) {
Expand All @@ -162,17 +163,21 @@ public int nextMaxSpawn(int calculated) {
if (spawnerIsFullHologram && !rerenderHologram) {
rerenderHologram = true;
spawnerIsFullHologram = false;
} else if (hologram != null && (calculated + spawned) == maxSpawnedResources) {
spawnerIsFullHologram = true;
if (certainPopularServerHolo) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_FULL_CERTAIN_POPULAR_SERVER));
} else {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_FULL));
} else if ((calculated + spawned) == maxSpawnedResources) {
spawnerLockedFull = true;
if (hologram != null) {
spawnerIsFullHologram = true;
if (certainPopularServerHolo) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_FULL_CERTAIN_POPULAR_SERVER));
} else {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_FULL));
}
}
}
return calculated;
}

spawnerLockedFull = true;
if (hologram != null && !spawnerIsFullHologram) {
spawnerIsFullHologram = true;
if (certainPopularServerHolo) {
Expand Down Expand Up @@ -245,7 +250,7 @@ public void setTier(int tier) {

@Override
public long getIntervalTicks() {
return currentInterval != null ? currentInterval.second().getBukkitTime(remainingTimeToSpawn) : 0;
return currentInterval != null ? currentInterval.second().getBukkitTime(currentCycle - elapsedTime % currentCycle) : 0;
}

@Override
Expand Down Expand Up @@ -311,25 +316,18 @@ private void prepareHolograms(List<BedWarsPlayer> viewers, boolean countdownHolo
}

public void destroy() {
if (hologramTask != null) {
hologramTask.cancel();
hologramTask = null;
}

if (spawningTask != null) {
spawningTask.cancel();
spawningTask = null;
if (runningTask != null) {
runningTask.cancel();
runningTask = null;
}

if (hologram != null) {
hologram.destroy();
hologram = null;
}

game = null;
amountPerSpawn = baseAmountPerSpawn;
spawnedItems.clear();
firstTick = true;
started = false;
disabled = false;
}
Expand All @@ -339,7 +337,6 @@ public void start(GameImpl game) {
return;
}

this.game = game;
this.amountPerSpawn = this.baseAmountPerSpawn;
this.tier = 0;
this.certainPopularServerHolo = hologramType == HologramType.CERTAIN_POPULAR_SERVER || (hologramType == HologramType.DEFAULT && game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.USE_CERTAIN_POPULAR_SERVER_LIKE_HOLOGRAMS_FOR_SPAWNERS, false));
Expand All @@ -355,106 +352,132 @@ public void start(GameImpl game) {
}

started = true;
firstTick = true;

changeInterval(Objects.requireNonNullElseGet(this.initialInterval, this.itemSpawnerType::getInterval));
}
// Old-new synchronous and probably more optimized spawner logic

public void changeInterval(Pair<Long, TaskerTime> time) {
if (!started || disabled) {
if (disabled) {
return;
}

if (hologramTask != null) {
hologramTask.cancel();
hologramTask = null;
}
this.currentInterval = Objects.requireNonNullElseGet(this.initialInterval, this.itemSpawnerType::getInterval);

if (spawningTask != null) {
spawningTask.cancel();
spawningTask = null;
if (runningTask != null) {
runningTask.cancel();
runningTask = null;
}

this.currentInterval = time;
// Precomputed options
boolean resetFullSpawnerCountdownAfterPicking = game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.RESET_FULL_SPAWNER_COUNTDOWN_AFTER_PICKING, true);
boolean useHolograms = hologramEnabled && game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.SPAWNER_HOLOGRAMS, false)
&& game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.SPAWNER_COUNTDOWN_HOLOGRAM, false);
boolean stopTeamSpawnersOnDie = game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.STOP_TEAM_SPAWNERS_ON_DIE, false);
this.currentCycle = currentInterval.second().getBukkitTime(currentInterval.getFirst());

// Cycle
this.elapsedTime = 0;
this.countdownDelay = 0;
this.spawnerLockedFull = false;
this.runningTask = this.location.tasker().runRepeatedly(taskItself -> {
if (this.firstTick) {
this.firstTick = false;
this.elapsedTime++;
return;
}

spawningTask = Tasker.runRepeatedly(DefaultThreads.GLOBAL_THREAD, () -> {
if (firstTick) {
firstTick = false;
return;
}
if (team != null && !game.isTeamActive(team) && stopTeamSpawnersOnDie) {
taskItself.cancel();
return;
}

if (team != null && !game.isTeamActive(team) && game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.STOP_TEAM_SPAWNERS_ON_DIE, false)) {
return;
}
long elapsedTime = this.elapsedTime - this.countdownDelay;
boolean preventSpawn = false;

var calculatedStack = (int) amountPerSpawn;
this.elapsedTime++;

/* fractional levels: have a weighted chance to increment */
/* for example, 3.1 -> 10% chance for 4 and 90% chance for 3 */
if ((amountPerSpawn % 1) != 0) {
if (Math.random() < (amountPerSpawn % 1)) {
calculatedStack++;
}
}
if (resetFullSpawnerCountdownAfterPicking && this.spawnerLockedFull) {
this.spawnedItems.removeIf(Entity::isDead);
if (this.maxSpawnedResources > getSpawnedItemsCount()) {
elapsedTime += this.countdownDelay;
this.countdownDelay = elapsedTime % currentCycle;
this.spawnerLockedFull = false;
preventSpawn = true;
} else {
return;
}
}

var resourceSpawnEvent = new ResourceSpawnEventImpl(game, this, itemSpawnerType, itemSpawnerType.getItem(calculatedStack));
EventManager.fire(resourceSpawnEvent);
if (useHolograms && elapsedTime % 20 == 0) {
long remainingTimeToSpawn = (currentCycle - elapsedTime % currentCycle) / 20;

if (resourceSpawnEvent.isCancelled()) {
return;
if (remainingTimeToSpawn == 0) {
remainingTimeToSpawn = currentCycle / 20;
}

if (!spawnerIsFullHologram) {
if (certainPopularServerHolo) {
if (currentInterval.first() > 1) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_COUNTDOWN_CERTAIN_POPULAR_SERVER).placeholder("seconds", currentInterval.second().getBukkitTime(remainingTimeToSpawn) / 20));
} else if (rerenderHologram) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_EVERY_SECOND));
rerenderHologram = false;
}
} else {
if (currentInterval.first() > 1) {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_COUNTDOWN).placeholder("seconds", currentInterval.second().getBukkitTime(remainingTimeToSpawn) / 20));
} else if (rerenderHologram) {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_EVERY_SECOND));
rerenderHologram = false;
}
}
}
}

var resource = resourceSpawnEvent.getResource();
if (preventSpawn || elapsedTime % currentCycle != 0) {
return;
}

resource = resource.withAmount(nextMaxSpawn(resource.getAmount()));
var calculatedStack = (int) amountPerSpawn;

if (resource.getAmount() > 0) {
var loc = this.location.add(0, 0.05, 0);
var item = Objects.requireNonNull(Entities.dropItem(resource, loc));
var spread = customSpread != null ? customSpread : itemSpawnerType.getSpread();
if (spread != 1.0) {
item.setVelocity(item.getVelocity().multiply(spread));
}
item.setPickupDelay(0, TimeUnit.SECONDS);
add(item);
}
}, currentInterval.first(), currentInterval.second());
/* fractional levels: have a weighted chance to increment */
/* for example, 3.1 -> 10% chance for 4 and 90% chance for 3 */
if ((amountPerSpawn % 1) != 0) {
if (Math.random() < (amountPerSpawn % 1)) {
calculatedStack++;
}
}

var resourceSpawnEvent = new ResourceSpawnEventImpl(game, this, itemSpawnerType, itemSpawnerType.getItem(calculatedStack));
EventManager.fire(resourceSpawnEvent);

if (hologramEnabled && game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.SPAWNER_HOLOGRAMS, false)
&& game.getConfigurationContainer().getOrDefault(GameConfigurationContainer.SPAWNER_COUNTDOWN_HOLOGRAM, false) ) {
hologramTask = Tasker.runAsyncDelayedAndRepeatedly(() -> {
if (disabled || firstTick) {
return;
}
if (resourceSpawnEvent.isCancelled()) {
return;
}

remainingTimeToSpawn = currentInterval.first() - elapsedTime;
var resource = resourceSpawnEvent.getResource();

if (remainingTimeToSpawn == 0) {
elapsedTime = 0;
remainingTimeToSpawn = currentInterval.first();
}
resource = resource.withAmount(nextMaxSpawn(resource.getAmount()));

elapsedTime++;

if (!spawnerIsFullHologram) {
if (certainPopularServerHolo) {
if (currentInterval.first() > 1) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_COUNTDOWN_CERTAIN_POPULAR_SERVER).placeholder("seconds", currentInterval.second().getBukkitTime(remainingTimeToSpawn) / 20));
} else if (rerenderHologram) {
hologram.replaceLine(2, Message.of(LangKeys.IN_GAME_SPAWNER_EVERY_SECOND));
rerenderHologram = false;
}
} else {
if (currentInterval.first() > 1) {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_COUNTDOWN).placeholder("seconds", currentInterval.second().getBukkitTime(remainingTimeToSpawn) / 20));
} else if (rerenderHologram) {
hologram.replaceLine(1, Message.of(LangKeys.IN_GAME_SPAWNER_EVERY_SECOND));
rerenderHologram = false;
}
}
}
}, 5, TaskerTime.TICKS, 20, TaskerTime.TICKS);
if (resource.getAmount() > 0) {
var loc = this.location.add(0, 0.05, 0);
var item = Objects.requireNonNull(Entities.dropItem(resource, loc));
var spread = customSpread != null ? customSpread : itemSpawnerType.getSpread();
if (spread != 1.0) {
item.setVelocity(item.getVelocity().multiply(spread));
}
item.setPickupDelay(0, TimeUnit.SECONDS);
add(item);
}
}, 1, TaskerTime.TICKS);
}

public void changeInterval(Pair<Long, TaskerTime> time) {
if (!started || disabled) {
return;
}

this.currentInterval = time;
this.currentCycle = time.second().getBukkitTime(time.first());
}

@Override
Expand Down

0 comments on commit b719f07

Please sign in to comment.