mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-23 19:35:10 -06:00
Small teardown tweaks for familiar POH entry
This commit is contained in:
parent
5a863732e9
commit
5d5354dc32
4 changed files with 209 additions and 55 deletions
|
|
@ -43,6 +43,11 @@ public final class HouseManager {
|
|||
*/
|
||||
private DynamicRegion dungeonRegion;
|
||||
|
||||
/**
|
||||
* If the current regions have been handed off to delayed teardown.
|
||||
*/
|
||||
private boolean teardownPending;
|
||||
|
||||
/**
|
||||
* The house location.
|
||||
*/
|
||||
|
|
@ -368,9 +373,24 @@ public final class HouseManager {
|
|||
}
|
||||
|
||||
ZoneBuilder.configure(zone);
|
||||
teardownPending = false;
|
||||
return houseRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current regions as unavailable while their delayed teardown is pending.
|
||||
* Stale regions from a house rebuild must not change the lifecycle of the replacement
|
||||
* regions now referenced by this manager.
|
||||
*
|
||||
* @param houseToRemove The house region being left.
|
||||
* @param dungeonToRemove The dungeon region paired with the house, if any.
|
||||
*/
|
||||
void beginTeardown(Region houseToRemove, Region dungeonToRemove) {
|
||||
if (houseRegion == houseToRemove && dungeonRegion == dungeonToRemove) {
|
||||
teardownPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicRegion getPreparedRegion() {
|
||||
ZoneBorders borders = DynamicRegion.reserveArea(8,8);
|
||||
DynamicRegion region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6);
|
||||
|
|
@ -708,7 +728,7 @@ public final class HouseManager {
|
|||
// return (houseRegion != null) || (dungeonRegion != null);
|
||||
//}
|
||||
public boolean isLoaded() {
|
||||
return (houseRegion != null && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive());
|
||||
return !teardownPending && ((houseRegion != null && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package content.global.skill.construction;
|
|||
|
||||
|
||||
import core.api.Container;
|
||||
import core.game.world.map.Location;
|
||||
import org.rs09.consts.Items;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.player.Player;
|
||||
|
|
@ -27,14 +26,14 @@ public final class HouseZone extends MapZone {
|
|||
private HouseManager house;
|
||||
|
||||
/**
|
||||
* The previous house region id.
|
||||
* The house region occupied before the latest transition.
|
||||
*/
|
||||
private int previousRegion = -1;
|
||||
private Region previousRegion;
|
||||
|
||||
/**
|
||||
* The previous dungeon region id.
|
||||
* The dungeon region paired with the previous house region.
|
||||
*/
|
||||
private int previousDungeon = -1;
|
||||
private Region previousDungeon;
|
||||
|
||||
/**
|
||||
* Constructs the house zone object.
|
||||
|
|
@ -55,11 +54,11 @@ public final class HouseZone extends MapZone {
|
|||
}
|
||||
|
||||
private void unregisterOldRegions() {
|
||||
if (previousRegion != -1) {
|
||||
unregisterRegion(previousRegion);
|
||||
if (previousRegion != null && RegionManager.getRegionCache().get(previousRegion.getId()) == previousRegion) {
|
||||
unregisterRegion(previousRegion.getId());
|
||||
}
|
||||
if (previousDungeon != -1) {
|
||||
unregisterRegion(previousDungeon);
|
||||
if (previousDungeon != null && RegionManager.getRegionCache().get(previousDungeon.getId()) == previousDungeon) {
|
||||
unregisterRegion(previousDungeon.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,9 +67,8 @@ public final class HouseZone extends MapZone {
|
|||
if (e instanceof Player) {
|
||||
Player pl = (Player) e;
|
||||
if (house == pl.getHouseManager()) {
|
||||
previousRegion = house.getHouseRegion().getId();
|
||||
if (house.getDungeonRegion() != null)
|
||||
previousDungeon = house.getDungeonRegion().getId();
|
||||
previousRegion = house.getHouseRegion();
|
||||
previousDungeon = house.getDungeonRegion();
|
||||
}
|
||||
registerLogoutListener(pl, "houselogout", (p) -> {
|
||||
p.setLocation(house.getLocation().getExitLocation());
|
||||
|
|
@ -95,56 +93,29 @@ public final class HouseZone extends MapZone {
|
|||
if (e instanceof Player) {
|
||||
Player p = (Player) e;
|
||||
|
||||
// if the player is moving between the dungeon and main house, don't trigger the leave sequence
|
||||
if (house.getDungeonRegion() != null && !logout) {
|
||||
// current loc
|
||||
Location dest = p.getProperties().getTeleportLocation();
|
||||
int currentRegionId = p.getLocation().getRegionId();
|
||||
int houseId = house.getHouseRegion().getId();
|
||||
int dungeonId = house.getDungeonRegion().getId();
|
||||
|
||||
// check if still in house
|
||||
boolean currentlyInHouse = (currentRegionId == houseId || currentRegionId == dungeonId);
|
||||
|
||||
// check if moving between house regions
|
||||
int destRegionId = (dest != null) ? dest.getRegionId() : -1;
|
||||
boolean movingToHouse = (destRegionId == houseId || destRegionId == dungeonId);
|
||||
|
||||
// if yes, return true and stop the leave sequence
|
||||
if (currentlyInHouse || movingToHouse) {
|
||||
return true;
|
||||
// RegionManager moves the player into the destination region before notifying the old
|
||||
// zone. Remaining in this manager's current regions is therefore an internal move, not
|
||||
// a real house exit. When the region instances changed, the move is a house rebuild
|
||||
// (such as a building-mode toggle), so only the stale instances should be torn down.
|
||||
if (!logout && (p.getViewport().getRegion() == house.getHouseRegion()
|
||||
|| p.getViewport().getRegion() == house.getDungeonRegion())) {
|
||||
if (previousRegion != house.getHouseRegion() || previousDungeon != house.getDungeonRegion()) {
|
||||
callFamiliar(p);
|
||||
submitRegionRemoval(previousRegion, previousDungeon);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Take the player's familiar along before the teardown pulse below wipes every NPC
|
||||
// still left inside the house - a wiped familiar is gone until relog (GL #2313). The
|
||||
// call either materializes it at the destination or parks it invisibly at the owner's
|
||||
// side until there is room for it.
|
||||
if (!logout && p.getFamiliarManager().hasFamiliar()) {
|
||||
p.getFamiliarManager().getFamiliar().call();
|
||||
|
||||
if (!logout) {
|
||||
callFamiliar(p);
|
||||
}
|
||||
|
||||
remove_items(p);
|
||||
// The below tears down the house if the owner was the one who left
|
||||
if (house == p.getHouseManager()) {
|
||||
house.expelGuests(p);
|
||||
int toRemove = previousRegion;
|
||||
int dungRemove = previousDungeon;
|
||||
submitWorldPulse(new Pulse(1) {
|
||||
public boolean pulse() {
|
||||
Region r = RegionManager.forId(toRemove);
|
||||
Region dr = dungRemove != -1 ? RegionManager.forId(dungRemove) : null;
|
||||
RegionManager.removeRegion(toRemove);
|
||||
unregisterRegion(toRemove);
|
||||
r.flagInactive();
|
||||
if (dungRemove != -1) {
|
||||
RegionManager.removeRegion(dungRemove);
|
||||
unregisterRegion(dungRemove);
|
||||
dr.flagInactive();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
house.beginTeardown(previousRegion, previousDungeon);
|
||||
submitRegionRemoval(previousRegion, previousDungeon);
|
||||
}
|
||||
// Clear logout listener and original-loc (if appropriate)
|
||||
clearLogoutListener(p, "houselogout");
|
||||
|
|
@ -156,6 +127,36 @@ public final class HouseZone extends MapZone {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the player's familiar along before a stale house region is cleared.
|
||||
*/
|
||||
private void callFamiliar(Player player) {
|
||||
if (player.getFamiliarManager().hasFamiliar()) {
|
||||
player.getFamiliarManager().getFamiliar().call();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes only the exact region instances captured while leaving. This prevents delayed
|
||||
* cleanup from evicting a replacement region that has since reused the same coordinates.
|
||||
*/
|
||||
private void submitRegionRemoval(Region houseToRemove, Region dungeonToRemove) {
|
||||
if (houseToRemove == null && dungeonToRemove == null) {
|
||||
return;
|
||||
}
|
||||
submitWorldPulse(new Pulse(1) {
|
||||
public boolean pulse() {
|
||||
if (houseToRemove != null) {
|
||||
RegionManager.removeRegion(houseToRemove);
|
||||
}
|
||||
if (dungeonToRemove != null) {
|
||||
RegionManager.removeRegion(dungeonToRemove);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void remove_items(Player p) {
|
||||
for (int item = Items.KETTLE_7688; item <= Items.CHEFS_DELIGHT_7755; item++) {//Removes all PoH versions of tea and beer barrel-related items
|
||||
removeAll(p, item, Container.INVENTORY);
|
||||
|
|
|
|||
|
|
@ -907,6 +907,25 @@ object RegionManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a region only if it is still the cached instance for its id.
|
||||
*
|
||||
* Delayed cleanup tasks must use this overload so they cannot remove a newer
|
||||
* dynamic region that has since been assigned the same coordinates.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun removeRegion(region: Region) {
|
||||
if (lock.tryLock() || LOCK.tryLock(10000, TimeUnit.MILLISECONDS)) {
|
||||
if (REGION_CACHE[region.id] === region) {
|
||||
REGION_CACHE.remove(region.id)
|
||||
}
|
||||
if (region.isActive) {
|
||||
region.flagInactive(true)
|
||||
}
|
||||
LOCK.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the regionCache.
|
||||
* @return The regionCache.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
package content.global.skill.construction
|
||||
|
||||
import TestUtils
|
||||
import content.global.skill.summoning.familiar.SteelTitanNPC
|
||||
import core.game.world.map.Region
|
||||
import core.game.world.map.RegionManager
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNotSame
|
||||
import org.junit.jupiter.api.Assertions.assertSame
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class HouseRegionLifecycleTests {
|
||||
companion object {
|
||||
init {
|
||||
TestUtils.preTestSetup()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildingModeRebuildKeepsReplacementHouseLoaded() {
|
||||
TestUtils.getMockPlayer("house-build-rebuild").use { player ->
|
||||
val house = player.houseManager
|
||||
val familiar = SteelTitanNPC(player, 7343)
|
||||
player.familiarManager.familiar = familiar
|
||||
familiar.init()
|
||||
house.createNewHouseAt(HouseLocation.RIMMINGTON)
|
||||
house.enter(player, false)
|
||||
player.walkingQueue.update()
|
||||
familiar.call()
|
||||
|
||||
val normalRegion = house.houseRegion
|
||||
assertTrue(house.isInHouse(player))
|
||||
assertTrue(familiar.isActive)
|
||||
assertSame(normalRegion, familiar.viewport.region)
|
||||
|
||||
house.toggleBuildingMode(player, true)
|
||||
val buildRegion = house.houseRegion
|
||||
assertNotSame(normalRegion, buildRegion)
|
||||
player.walkingQueue.update()
|
||||
TestUtils.advanceTicks(2, false)
|
||||
|
||||
assertTrue(house.isBuildingMode)
|
||||
assertTrue(house.isLoaded)
|
||||
assertTrue(house.isInHouse(player))
|
||||
assertFalse(normalRegion.isActive)
|
||||
assertSame(buildRegion, RegionManager.regionCache[buildRegion.id])
|
||||
assertTrue(familiar.isActive)
|
||||
assertSame(buildRegion, familiar.viewport.region)
|
||||
|
||||
house.toggleBuildingMode(player, false)
|
||||
val replacementNormalRegion = house.houseRegion
|
||||
assertNotSame(buildRegion, replacementNormalRegion)
|
||||
player.walkingQueue.update()
|
||||
TestUtils.advanceTicks(2, false)
|
||||
|
||||
assertFalse(house.isBuildingMode)
|
||||
assertTrue(house.isLoaded)
|
||||
assertTrue(house.isInHouse(player))
|
||||
assertFalse(buildRegion.isActive)
|
||||
assertSame(replacementNormalRegion, RegionManager.regionCache[replacementNormalRegion.id])
|
||||
assertTrue(familiar.isActive)
|
||||
assertSame(replacementNormalRegion, familiar.viewport.region)
|
||||
|
||||
HouseManager.leave(player)
|
||||
player.walkingQueue.update()
|
||||
TestUtils.advanceTicks(2, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun regionPendingTeardownIsNotReusedByImmediateReentry() {
|
||||
TestUtils.getMockPlayer("house-region-reentry").use { player ->
|
||||
val house = player.houseManager
|
||||
house.createNewHouseAt(HouseLocation.RIMMINGTON)
|
||||
house.enter(player, false)
|
||||
player.walkingQueue.update()
|
||||
val oldRegion = house.houseRegion
|
||||
|
||||
HouseManager.leave(player)
|
||||
player.walkingQueue.update()
|
||||
assertFalse(house.isLoaded)
|
||||
|
||||
house.enter(player, false)
|
||||
val replacement = house.houseRegion
|
||||
assertNotSame(oldRegion, replacement)
|
||||
player.walkingQueue.update()
|
||||
TestUtils.advanceTicks(2, false)
|
||||
|
||||
assertTrue(house.isLoaded)
|
||||
assertTrue(house.isInHouse(player))
|
||||
assertFalse(oldRegion.isActive)
|
||||
assertSame(replacement, RegionManager.regionCache[replacement.id])
|
||||
|
||||
HouseManager.leave(player)
|
||||
player.walkingQueue.update()
|
||||
TestUtils.advanceTicks(2, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delayedRemovalDoesNotEvictReplacementAtSameRegionId() {
|
||||
val house = HouseManager()
|
||||
house.createNewHouseAt(HouseLocation.RIMMINGTON)
|
||||
val oldRegion = house.construct()
|
||||
val replacement = Region(oldRegion.x, oldRegion.y)
|
||||
RegionManager.addRegion(replacement.id, replacement)
|
||||
|
||||
RegionManager.removeRegion(oldRegion)
|
||||
|
||||
assertSame(replacement, RegionManager.regionCache[replacement.id])
|
||||
RegionManager.removeRegion(replacement.id)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue