New dynamic region abstraction, consumed in construction

This commit is contained in:
Ceikry
2022-05-03 23:18:11 +00:00
committed by Ryan
parent 8438ecdbd7
commit a5f9c64dbb
26 changed files with 745 additions and 177 deletions
@@ -166,7 +166,7 @@ public final class BuildingUtils {
int roomX = object.getLocation().getChunkX(); int roomX = object.getLocation().getChunkX();
int roomY = object.getLocation().getChunkY(); int roomY = object.getLocation().getChunkY();
int z = object.getLocation().getZ(); int z = object.getLocation().getZ();
Region region = player.getHouseManager().getRegion(); Region region = player.getHouseManager().getHouseRegion();
if (HouseManager.isInDungeon(player)) { if (HouseManager.isInDungeon(player)) {
region = player.getHouseManager().getDungeonRegion(); region = player.getHouseManager().getDungeonRegion();
z = 3; z = 3;
@@ -265,7 +265,7 @@ public final class BuildingUtils {
Hotspot h = r.getStairs(); Hotspot h = r.getStairs();
if (h != null) { if (h != null) {
h.setDecorationIndex(decIndex); h.setDecorationIndex(decIndex);
Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getRegion(); Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getHouseRegion();
if (reg == null) { if (reg == null) {
continue; continue;
} }
@@ -352,7 +352,7 @@ public final class BuildingUtils {
} }
Location l = object.getLocation(); Location l = object.getLocation();
Room room = player.getHouseManager().getRooms()[l.getZ()][l.getChunkX()][l.getChunkY()]; Room room = player.getHouseManager().getRooms()[l.getZ()][l.getChunkX()][l.getChunkY()];
Region region = player.getHouseManager().getRegion(); Region region = player.getHouseManager().getHouseRegion();
if (HouseManager.isInDungeon(player)) { if (HouseManager.isInDungeon(player)) {
region = player.getHouseManager().getDungeonRegion(); region = player.getHouseManager().getDungeonRegion();
room = player.getHouseManager().getRooms()[3][l.getChunkX()][l.getChunkY()]; room = player.getHouseManager().getRooms()[3][l.getChunkX()][l.getChunkY()];
@@ -396,7 +396,7 @@ public final class BuildingUtils {
Hotspot h = r.getStairs(); Hotspot h = r.getStairs();
if (h != null) { if (h != null) {
h.setDecorationIndex(-1); h.setDecorationIndex(-1);
Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getRegion(); Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getHouseRegion();
if (reg == null) { if (reg == null) {
continue; continue;
} }
@@ -589,7 +589,7 @@ public final class BuildingUtils {
if (HouseManager.isInDungeon(player)) { if (HouseManager.isInDungeon(player)) {
z = 3; z = 3;
} }
if (player.getHouseManager().hasRoom(z, location[0], location[1])) { if (player.getHouseManager().hasRoomAt(z, location[0], location[1])) {
return location; return location;
} }
return null; return null;
@@ -666,7 +666,7 @@ public final class BuildingUtils {
else if (player.getHouseManager().hasExit(z, roomX - 1, roomY, Direction.EAST)) { else if (player.getHouseManager().hasExit(z, roomX - 1, roomY, Direction.EAST)) {
exits[2] = 1; exits[2] = 1;
} }
else if (player.getHouseManager().hasRoom(z, roomX - 1, roomY)) { else if (player.getHouseManager().hasRoomAt(z, roomX - 1, roomY)) {
exits[2] = -1; exits[2] = -1;
} }
if (roomY == 7) { if (roomY == 7) {
@@ -675,7 +675,7 @@ public final class BuildingUtils {
else if (player.getHouseManager().hasExit(z, roomX, roomY + 1, Direction.SOUTH)) { else if (player.getHouseManager().hasExit(z, roomX, roomY + 1, Direction.SOUTH)) {
exits[3] = 1; exits[3] = 1;
} }
else if (player.getHouseManager().hasRoom(z, roomX, roomY + 1)) { else if (player.getHouseManager().hasRoomAt(z, roomX, roomY + 1)) {
exits[3] = -1; exits[3] = -1;
} }
if (roomX == 7) { if (roomX == 7) {
@@ -684,7 +684,7 @@ public final class BuildingUtils {
else if (player.getHouseManager().hasExit(z, roomX + 1, roomY, Direction.WEST)) { else if (player.getHouseManager().hasExit(z, roomX + 1, roomY, Direction.WEST)) {
exits[0] = 1; exits[0] = 1;
} }
else if (player.getHouseManager().hasRoom(z, roomX + 1, roomY)) { else if (player.getHouseManager().hasRoomAt(z, roomX + 1, roomY)) {
exits[0] = -1; exits[0] = -1;
} }
if (roomY == 0) { if (roomY == 0) {
@@ -693,7 +693,7 @@ public final class BuildingUtils {
else if (player.getHouseManager().hasExit(z, roomX, roomY - 1, Direction.NORTH)) { else if (player.getHouseManager().hasExit(z, roomX, roomY - 1, Direction.NORTH)) {
exits[1] = 1; exits[1] = 1;
} }
else if (player.getHouseManager().hasRoom(z, roomX, roomY - 1)) { else if (player.getHouseManager().hasRoomAt(z, roomX, roomY - 1)) {
exits[1] = -1; exits[1] = -1;
} }
return exits; return exits;
@@ -4,6 +4,8 @@ package core.game.node.entity.skill.construction;
//import org.arios.game.content.global.DeadmanTimedAction; //import org.arios.game.content.global.DeadmanTimedAction;
//import org.arios.game.node.entity.player.info.login.SavingModule; //import org.arios.game.node.entity.player.info.login.SavingModule;
import api.regionspec.RegionSpecification;
import api.regionspec.contracts.FillChunkContract;
import core.game.content.dialogue.FacialExpression; import core.game.content.dialogue.FacialExpression;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.player.link.audio.Audio;
@@ -15,6 +17,8 @@ import core.game.world.map.build.DynamicRegion;
import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBorders;
import core.game.world.map.zone.ZoneBuilder; import core.game.world.map.zone.ZoneBuilder;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import rs09.game.node.entity.skill.construction.Hotspot; import rs09.game.node.entity.skill.construction.Hotspot;
@@ -24,6 +28,9 @@ import rs09.game.world.GameWorld;
import java.awt.*; import java.awt.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static api.regionspec.RegionSpecificationKt.fillWith;
import static api.regionspec.RegionSpecificationKt.using;
/** /**
* Manages the player's house. * Manages the player's house.
* @author Emperor * @author Emperor
@@ -34,12 +41,12 @@ public final class HouseManager {
/** /**
* The current region. * The current region.
*/ */
private DynamicRegion region; private DynamicRegion houseRegion;
/** /**
* The current region. * The current region.
*/ */
private DynamicRegion dungeon; private DynamicRegion dungeonRegion;
/** /**
* The house location. * The house location.
@@ -174,58 +181,73 @@ public final class HouseManager {
} }
} }
/**
* Enters the player's house.
* @param player The player entering.
* @param buildingMode If building mode is enabled.
* @param teleport if the entry was a teleport.
*/
public void enter(final Player player, boolean buildingMode, boolean teleport) {
enter(player, buildingMode);
}
/** /**
* Enter's the player's house. * Enter's the player's house.
* @param player * @param player
* @param buildingMode * @param buildingMode
*/ */
public void enter(final Player player, boolean buildingMode) { public void enter(final Player player, boolean buildingMode) {
if (HouseManager.this.buildingMode != buildingMode || !isLoaded()) { if (this.buildingMode != buildingMode || !isLoaded()) {
HouseManager.this.buildingMode = buildingMode; this.buildingMode = buildingMode;
construct(); construct();
} }
player.setAttribute("poh_entry", HouseManager.this); player.setAttribute("poh_entry", HouseManager.this);
player.lock(1); player.lock(1);
player.sendMessage("House location: " + region.getBaseLocation() + ", entry: " + getEnterLocation()); player.sendMessage("House location: " + houseRegion.getBaseLocation() + ", entry: " + getEnterLocation());
player.getProperties().setTeleportLocation(getEnterLocation()); player.getProperties().setTeleportLocation(getEnterLocation());
openLoadInterface(player);
checkForAndSpawnServant(player);
updateVarbits(player, buildingMode);
unlockMusicTrack(player);
}
private void openLoadInterface(Player player) {
player.getInterfaceManager().openComponent(399); player.getInterfaceManager().openComponent(399);
player.getConfigManager().set(261, buildingMode);
player.getConfigManager().set(262, getRoomAmount());
player.getAudioManager().send(new Audio(984)); player.getAudioManager().send(new Audio(984));
player.getMusicPlayer().unlock(454, true); submitCloseLoadInterfacePulse(player);
}
private void submitCloseLoadInterfacePulse(Player player) {
GameWorld.getPulser().submit(new Pulse(1, player) { GameWorld.getPulser().submit(new Pulse(1, player) {
@Override @Override
public boolean pulse() { public boolean pulse() {
if (hasServant()){
spawnServant();
if (servant.isGreet()){
player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Welcome.");
}
}
// player.getInterfaceManager().switchWindowMode(1);
player.getInterfaceManager().close(); player.getInterfaceManager().close();
return true; return true;
} }
}); });
} }
private void checkForAndSpawnServant(Player player) {
if(!hasServant()) return;
GameWorld.getPulser().submit(new Pulse(1, player) {
@Override
public boolean pulse() {
spawnServant();
if (servant.isGreet()){
player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Welcome.");
}
return true;
}
});
}
private void updateVarbits(Player player, boolean build) {
player.varpManager.get(261).setVarbit(0, build ? 1 : 0);
player.varpManager.get(262).setVarbit(0, getRoomAmount());
}
private void unlockMusicTrack(Player player) {
player.getMusicPlayer().unlock(454, true);
}
/** /**
* Leaves this house. * Leaves this house.
* @param player The player leaving. * @param player The player leaving.
*/ */
public static void leave(Player player) { public static void leave(Player player) {
HouseManager house = player.getAttribute("poh_entry", player.getHouseManager()); HouseManager house = player.getAttribute("poh_entry", player.getHouseManager());
if (house.getRegion() == null){ if (house.getHouseRegion() == null){
return; return;
} }
if (house.isInHouse(player)) { if (house.isInHouse(player)) {
@@ -260,17 +282,14 @@ public final class HouseManager {
* @param buildingMode If building mode should be enabled. * @param buildingMode If building mode should be enabled.
*/ */
public void reload(Player player, boolean buildingMode) { public void reload(Player player, boolean buildingMode) {
DynamicRegion r = region; int diffX = player.getLocation().getLocalX();
if ((player.getViewport().getRegion() == dungeon)) { int diffY = player.getLocation().getLocalY();
r = dungeon; int diffZ = player.getLocation().getZ();
} boolean inDungeon = player.getViewport().getRegion() == dungeonRegion;
int diffX = player.getLocation().getX() - r.getBaseLocation().getX(); this.buildingMode = buildingMode;
int diffY = player.getLocation().getY() - r.getBaseLocation().getY(); construct();
int diffZ = player.getLocation().getZ() - r.getBaseLocation().getZ(); Location newLoc = (dungeonRegion == null ? houseRegion : (inDungeon ? dungeonRegion : houseRegion)).getBaseLocation().transform(diffX,diffY,diffZ);
region = null; player.getProperties().setTeleportLocation(newLoc);
dungeon = null;
enter(player, buildingMode, false);
player.getProperties().setTeleportLocation((player.getViewport().getRegion() == dungeon ? dungeon : region).getBaseLocation().transform(diffX, diffY, diffZ));
} }
/** /**
@@ -279,15 +298,15 @@ public final class HouseManager {
*/ */
public void expelGuests(Player player) { public void expelGuests(Player player) {
if (isLoaded()) { if (isLoaded()) {
for (RegionPlane plane : region.getPlanes()) { for (RegionPlane plane : houseRegion.getPlanes()) {
for (Player p : plane.getPlayers()) { for (Player p : plane.getPlayers()) {
if (p != player) { if (p != player) {
leave(p); leave(p);
} }
} }
} }
if (dungeon != null) { if (dungeonRegion != null) {
for (RegionPlane plane : dungeon.getPlanes()) { for (RegionPlane plane : dungeonRegion.getPlanes()) {
for (Player p : plane.getPlayers()) { for (Player p : plane.getPlayers()) {
if (p != player) { if (p != player) {
leave(p); leave(p);
@@ -303,7 +322,7 @@ public final class HouseManager {
* @return The entering location. * @return The entering location.
*/ */
public Location getEnterLocation() { public Location getEnterLocation() {
if (region == null) { if (houseRegion == null) {
SystemLogger.logErr("House wasn't constructed yet!"); SystemLogger.logErr("House wasn't constructed yet!");
return null; return null;
} }
@@ -315,7 +334,7 @@ public final class HouseManager {
if (h.getDecorationIndex() > -1) { if (h.getDecorationIndex() > -1) {
Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()]; Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()];
if (d == Decoration.PORTAL) { if (d == Decoration.PORTAL) {
return region.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0); return houseRegion.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0);
} }
} }
} }
@@ -361,7 +380,7 @@ public final class HouseManager {
* Creates the default house. * Creates the default house.
* @param location The house location. * @param location The house location.
*/ */
public void create(HouseLocation location) { public void createNewHouseAt(HouseLocation location) {
clearRooms(); clearRooms();
Room room = rooms[0][4][3] = new Room(RoomProperties.GARDEN); Room room = rooms[0][4][3] = new Room(RoomProperties.GARDEN);
room.configure(style); room.configure(style);
@@ -374,60 +393,100 @@ public final class HouseManager {
* @return The region. * @return The region.
*/ */
public DynamicRegion construct() { public DynamicRegion construct() {
houseRegion = getPreparedRegion();
configureRoofs();
prepareHouseChunks(style, houseRegion, buildingMode, rooms);
if (hasDungeon()) {
dungeonRegion = getPreparedRegion();
prepareDungeonChunks(style, dungeonRegion, houseRegion, buildingMode, rooms[3]);
}
ZoneBuilder.configure(zone);
return houseRegion;
}
private DynamicRegion getPreparedRegion() {
ZoneBorders borders = DynamicRegion.reserveArea(8,8);
DynamicRegion region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6);
region.setBorders(borders);
region.setUpdateAllPlanes(true);
RegionManager.addRegion(region.getId(), region);
return region;
}
private class RoomLoadContract extends FillChunkContract {
Room[][][] rooms;
HouseManager manager;
boolean buildingMode;
public RoomLoadContract(HouseManager manager, boolean buildingMode, Room[][][] rooms) {
this.rooms = rooms;
this.manager = manager;
this.buildingMode = buildingMode;
}
@Override
public BuildRegionChunk getChunk(int x, int y, int plane, @NotNull DynamicRegion dyn) {
BuildRegionChunk chunk = rooms[plane][x][y].getChunk().copy(dyn.getPlanes()[plane]);
return chunk;
}
@Override
public void afterSetting(@Nullable BuildRegionChunk chunk, int x, int y, int plane, @NotNull DynamicRegion dyn) {
rooms[plane][x][y].loadDecorations(dyn != manager.dungeonRegion ? plane : 3, chunk, manager);
}
}
private void prepareHouseChunks(HousingStyle style, DynamicRegion target, boolean buildingMode, Room[][][] rooms) {
Region from = RegionManager.forId(style.getRegionId()); Region from = RegionManager.forId(style.getRegionId());
Region.load(from, true); Region.load(from, true);
RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(1, 0); RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(1, 0);
RegionChunk defaultSkyChunk = from.getPlanes()[1].getRegionChunk(0,0); RegionChunk defaultSkyChunk = from.getPlanes()[1].getRegionChunk(0,0);
ZoneBorders borders = DynamicRegion.reserveArea(8,8);
region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, rooms);
region.setBorders(borders); RegionSpecification spec = new RegionSpecification(
region.setUpdateAllPlanes(true); using(target),
RegionManager.addRegion(region.getId(), region); fillWith(defaultChunk)
configureRoofs(); .from(from)
for (int z = 0; z < 4; z++) { .onPlanes(0)
for (int x = 0; x < 8; x++) { .onCondition((destX, destY, plane) -> rooms[plane][destX][destY] == null),
for (int y = 0; y < 8; y++) { fillWith((RegionChunk) null)
if(z == 3){ .from(from)
region.replaceChunk(z, x, y, defaultSkyChunk.copy(region.getPlanes()[z]), from); .onPlanes(1,2)
continue; .onCondition((destX, destY, plane) -> rooms[plane][destX][destY] == null),
} fillWith(defaultSkyChunk)
Room room = rooms[z][x][y]; .from(from)
if (room != null) { .onPlanes(3),
if (room.getProperties().isRoof() && buildingMode) { loadRooms
continue; .from(from)
} .onPlanes(0,1,2)
BuildRegionChunk copy = room.getChunk().copy(region.getPlanes()[z]); .onCondition((destX,destY,plane) -> rooms[plane][destX][destY] != null)
region.replaceChunk(z, x, y, copy, from); );
room.loadDecorations(z, copy, this);
} else { spec.build();
region.replaceChunk(z, x, y, z != 0 ? null : defaultChunk.copy(region.getPlanes()[0]), from); }
}
} private void prepareDungeonChunks(HousingStyle style, DynamicRegion target, DynamicRegion house, boolean buildingMode, Room[][] rooms) {
} Region from = RegionManager.forId(style.getRegionId());
} Region.load(from, true);
if (hasDungeon()) { RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(3, 0);
defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(3, 0);
borders = DynamicRegion.reserveArea(8, 8); RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, new Room[][][]{rooms});
dungeon = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); RegionSpecification spec = new RegionSpecification(
dungeon.setBorders(borders); using(target),
dungeon.setUpdateAllPlanes(true); fillWith((x,y,plane,region) -> buildingMode ? null : defaultChunk)
RegionManager.addRegion(dungeon.getId(), dungeon); .from(from)
for (int x = 0; x < 8; x++) { .onPlanes(0)
for (int y = 0; y < 8; y++) { .onCondition((destX, destY, plane) -> rooms[destX][destY] == null),
Room room = rooms[3][x][y]; loadRooms
if (hasRoom(3, x, y)) { .from(from)
BuildRegionChunk copy = room.getChunk().copy(dungeon.getPlanes()[0]); .onPlanes(0)
dungeon.replaceChunk(0, x, y, copy, from); .onCondition((destX, destY, plane) -> rooms[destX][destY] != null)
room.loadDecorations(3, copy, this); );
} else {
dungeon.replaceChunk(0, x, y, buildingMode ? null : defaultChunk.copy(dungeon.getPlanes()[0]), from); spec.build();
} house.link(target);
}
}
region.link(dungeon);
}
ZoneBuilder.configure(zone);
return region;
} }
/** /**
@@ -457,7 +516,7 @@ public final class HouseManager {
*/ */
public Room getRoom(Location l) { public Room getRoom(Location l) {
int z = l.getZ(); int z = l.getZ();
if (dungeon != null && l.getRegionId() == dungeon.getId()) { if (dungeonRegion != null && l.getRegionId() == dungeonRegion.getId()) {
z = 3; z = 3;
} }
return rooms[z][l.getChunkX()][l.getChunkY()]; return rooms[z][l.getChunkX()][l.getChunkY()];
@@ -512,7 +571,7 @@ public final class HouseManager {
* @param roomY The room y-coordinate. * @param roomY The room y-coordinate.
* @return {@code True} if so. * @return {@code True} if so.
*/ */
public boolean hasRoom(int z, int roomX, int roomY) { public boolean hasRoomAt(int z, int roomX, int roomY) {
Room room = rooms[z][roomX][roomY]; Room room = rooms[z][roomX][roomY];
return room != null && !room.getProperties().isRoof(); return room != null && !room.getProperties().isRoof();
} }
@@ -527,7 +586,7 @@ public final class HouseManager {
} }
int diffX = player.getLocation().getLocalX(); int diffX = player.getLocation().getLocalX();
int diffY = player.getLocation().getLocalY(); int diffY = player.getLocation().getLocalY();
player.getProperties().setTeleportLocation(dungeon.getBaseLocation().transform(diffX, diffY, 0)); player.getProperties().setTeleportLocation(dungeonRegion.getBaseLocation().transform(diffX, diffY, 0));
} }
/** /**
@@ -668,7 +727,7 @@ public final class HouseManager {
* @return {@code True} if so. * @return {@code True} if so.
*/ */
public boolean isInHouse(Player player) { public boolean isInHouse(Player player) {
return isLoaded() && (player.getViewport().getRegion() == region || player.getViewport().getRegion() == dungeon); return isLoaded() && (player.getViewport().getRegion() == houseRegion || player.getViewport().getRegion() == dungeonRegion);
} }
/** /**
@@ -677,7 +736,7 @@ public final class HouseManager {
* @return {@code True} if so. * @return {@code True} if so.
*/ */
public static boolean isInDungeon(Player player) { public static boolean isInDungeon(Player player) {
return player.getViewport().getRegion() == player.getHouseManager().dungeon; return player.getViewport().getRegion() == player.getHouseManager().dungeonRegion;
} }
/** /**
@@ -685,7 +744,7 @@ public final class HouseManager {
* @return {@code True} if an active region for the house exists. * @return {@code True} if an active region for the house exists.
*/ */
public boolean isLoaded() { public boolean isLoaded() {
return region != null && region.isActive() || dungeon != null && dungeon.isActive(); return (houseRegion != null) || (dungeonRegion != null);
} }
/** /**
@@ -764,8 +823,8 @@ public final class HouseManager {
* Gets the region. * Gets the region.
* @return The region. * @return The region.
*/ */
public Region getRegion() { public DynamicRegion getHouseRegion() {
return region; return houseRegion;
} }
/** /**
@@ -773,7 +832,7 @@ public final class HouseManager {
* @return The dungeon region. * @return The dungeon region.
*/ */
public Region getDungeonRegion() { public Region getDungeonRegion() {
return dungeon; return dungeonRegion;
} }
/** /**
@@ -44,9 +44,9 @@ public final class HouseZone extends MapZone {
if (previousDungeon != -1) { if (previousDungeon != -1) {
unregisterRegion(previousDungeon); unregisterRegion(previousDungeon);
} }
registerRegion(house.getRegion().getId()); registerRegion(house.getHouseRegion().getId());
if (house.getDungeonRegion() != null) { if (house.getDungeonRegion() != null) {
registerRegion(house.getRegion().getId()); registerRegion(house.getHouseRegion().getId());
} }
} }
@@ -116,7 +116,7 @@ public final class PortalOptionPlugin extends OptionHandler {
player.sendMessage("<col=FF0000>Speak with an estate agent to change your house location."); player.sendMessage("<col=FF0000>Speak with an estate agent to change your house location.");
break; break;
} }
player.getHouseManager().enter(player, buttonId == 2, true); player.getHouseManager().enter(player, buttonId == 2);
break; break;
case 3: case 3:
if(player.getIronmanManager().isIronman()){ if(player.getIronmanManager().isIronman()){
@@ -147,7 +147,7 @@ public final class PortalOptionPlugin extends OptionHandler {
return Unit.INSTANCE; return Unit.INSTANCE;
} }
p.setAttribute("poh_owner", (String) value); p.setAttribute("poh_owner", (String) value);
p.getHouseManager().enter(player, false, false); p.getHouseManager().enter(player, false);
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
break; break;
@@ -68,7 +68,7 @@ public final class RemovalDialogue extends DialoguePlugin {
public boolean handle(int interfaceId, int buttonId) { public boolean handle(int interfaceId, int buttonId) {
if (stage == 0) { if (stage == 0) {
if (buttonId == 1) { if (buttonId == 1) {
if (plane == 0 && player.getHouseManager().hasRoom(1, pos[0], pos[1])) { if (plane == 0 && player.getHouseManager().hasRoomAt(1, pos[0], pos[1])) {
interpreter.sendPlainMessage(false, "You can't remove a room supporting another room."); interpreter.sendPlainMessage(false, "You can't remove a room supporting another room.");
stage = 1; stage = 1;
return true; return true;
@@ -103,7 +103,7 @@ public final class RoomBuilder {
break; break;
case RECURSIVE: case RECURSIVE:
room.setAllDecorationIndex(hotspot.getDecorationIndex(deco), h.getHotspot()); room.setAllDecorationIndex(hotspot.getDecorationIndex(deco), h.getHotspot());
Scenery[][] objects = player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects(); Scenery[][] objects = player.getHouseManager().getHouseRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects();
for (int j = 0; j < objects.length; j++) { for (int j = 0; j < objects.length; j++) {
for (int k = 0; k < objects[j].length; k++) { for (int k = 0; k < objects[j].length; k++) {
Scenery go = objects[j][k]; Scenery go = objects[j][k];
@@ -115,7 +115,7 @@ public final class RoomBuilder {
break; break;
case LINKED: case LINKED:
BuildHotspot[] linkedHotspots = BuildHotspot.getLinkedHotspots(h.getHotspot()); BuildHotspot[] linkedHotspots = BuildHotspot.getLinkedHotspots(h.getHotspot());
BuildRegionChunk chunk = (BuildRegionChunk) player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3]; BuildRegionChunk chunk = (BuildRegionChunk) player.getHouseManager().getHouseRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3];
for (int x = 0; x < 8; x++) { for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) { for (int y = 0; y < 8; y++) {
for(BuildHotspot bh : linkedHotspots) { for(BuildHotspot bh : linkedHotspots) {
@@ -151,7 +151,7 @@ public final class RoomBuilder {
return; return;
case RECURSIVE: case RECURSIVE:
room.setAllDecorationIndex(-1, hotspot.getHotspot()); room.setAllDecorationIndex(-1, hotspot.getHotspot());
Scenery[][] objects = player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects(); Scenery[][] objects = player.getHouseManager().getHouseRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects();
for (int j = 0; j < objects.length; j++) { for (int j = 0; j < objects.length; j++) {
for (int k = 0; k < objects[j].length; k++) { for (int k = 0; k < objects[j].length; k++) {
Scenery go = objects[j][k]; Scenery go = objects[j][k];
@@ -132,7 +132,7 @@ public final class StaircasePlugin extends OptionHandler {
} else { } else {
Location destination = l.transform(0, 0, z); Location destination = l.transform(0, 0, z);
if (player.getViewport().getRegion() == house.getDungeonRegion()) { if (player.getViewport().getRegion() == house.getDungeonRegion()) {
destination = house.getRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); destination = house.getHouseRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0);
} }
else if (plane == 3) { else if (plane == 3) {
destination = house.getDungeonRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); destination = house.getDungeonRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0);
@@ -523,7 +523,7 @@ public class HouseServantDialogue extends DialoguePlugin {
@Override @Override
public boolean pulse() { public boolean pulse() {
if (player == null || player.getHouseManager().getRegion() != player.getViewport().getRegion()) { //TODO: Check if in dungeon? if (player == null || player.getHouseManager().getHouseRegion() != player.getViewport().getRegion()) { //TODO: Check if in dungeon?
return true; return true;
} }
int amt = player.getBank().getAmount(item.getId()); int amt = player.getBank().getAmount(item.getId());
@@ -10,6 +10,9 @@ import core.game.world.map.*;
import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.RegionZone;
import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBorders;
import core.game.world.map.zone.impl.MultiwayCombatZone; import core.game.world.map.zone.impl.MultiwayCombatZone;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import rs09.game.system.SystemLogger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -78,6 +81,13 @@ public final class DynamicRegion extends Region {
this.chunks = new RegionChunk[4][SIZE >> 3][SIZE >> 3]; this.chunks = new RegionChunk[4][SIZE >> 3][SIZE >> 3];
} }
public DynamicRegion(@NotNull ZoneBorders borders) {
this(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6);
setBorders(borders);
setUpdateAllPlanes(true);
RegionManager.addRegion(getId(), this);
}
/** /**
* Creates a dynamic region copy of the region id. * Creates a dynamic region copy of the region id.
* @param regionId The region id. * @param regionId The region id.
@@ -187,6 +187,7 @@ public final class PacketRepository {
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
public static void send(Class<? extends OutgoingPacket> clazz, Context context) { public static void send(Class<? extends OutgoingPacket> clazz, Context context) {
if(context.getPlayer().getSession() == null) return;
OutgoingPacket p = OUTGOING_PACKETS.get(clazz); OutgoingPacket p = OUTGOING_PACKETS.get(clazz);
if (p == null) { if (p == null) {
SystemLogger.logErr("Invalid outgoing packet [handler=" + clazz + ", context=" + context + "]."); SystemLogger.logErr("Invalid outgoing packet [handler=" + clazz + ", context=" + context + "].");
@@ -0,0 +1,34 @@
package api.regionspec
import api.regionspec.contracts.*
import core.game.world.map.Region
import core.game.world.map.RegionChunk
import core.game.world.map.RegionPlane
import core.game.world.map.build.DynamicRegion
class RegionSpecification(val regionContract: RegionSpecContract = EmptyRegionContract(), vararg val chunkContracts: ChunkSpecContract = arrayOf(EmptyChunkContract())) {
constructor(vararg chunkContracts: ChunkSpecContract) : this(EmptyRegionContract(), *chunkContracts)
fun build(): DynamicRegion {
val dyn = regionContract.instantiateRegion()
Region.load(dyn)
chunkContracts.forEach { it.populateChunks(dyn) }
return dyn
}
}
fun fillWith(chunk: RegionChunk?): FillChunkContract {
return FillChunkContract(chunk)
}
fun fillWith(delegate: (Int, Int, Int, Region) -> RegionChunk?) : FillChunkContract {
return FillChunkContract(delegate)
}
fun copyOf(regionId: Int): RegionSpecContract {
return CloneRegionContract(regionId)
}
fun using(region: DynamicRegion) : UseExistingRegionContract {
return UseExistingRegionContract(region)
}
@@ -0,0 +1,7 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
interface ChunkSpecContract {
fun populateChunks(dyn: DynamicRegion)
}
@@ -0,0 +1,9 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
class CloneRegionContract(val regionId: Int) : RegionSpecContract {
override fun instantiateRegion(): DynamicRegion {
return DynamicRegion.create(regionId)
}
}
@@ -0,0 +1,7 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
class EmptyChunkContract : ChunkSpecContract {
override fun populateChunks(dyn: DynamicRegion) {}
}
@@ -0,0 +1,11 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
class EmptyRegionContract : RegionSpecContract {
override fun instantiateRegion(): DynamicRegion {
val borders = DynamicRegion.reserveArea(8,8)
val dyn = DynamicRegion(borders)
return dyn
}
}
@@ -0,0 +1,54 @@
package api.regionspec.contracts
import core.game.world.map.BuildRegionChunk
import core.game.world.map.Region
import core.game.world.map.RegionChunk
import core.game.world.map.build.DynamicRegion
open class FillChunkContract(var chunk: RegionChunk? = null) : ChunkSpecContract {
constructor(chunk: (Int, Int, Int, Region) -> RegionChunk?) : this(null) {this.chunkDelegate = chunk}
lateinit var sourceRegion: Region
var planes: IntArray = intArrayOf(0)
var replaceCondition: (Int,Int,Int) -> Boolean = {_,_,_ -> true}
var chunkDelegate: (Int, Int, Int, Region) -> RegionChunk? = {_,_,_,_ -> chunk}
override fun populateChunks(dyn: DynamicRegion) {
for(plane in planes) {
for(x in 0 until 8)
for(y in 0 until 8)
if(replaceCondition.invoke(x,y,plane)) {
val chunk = getChunk(x,y,plane,dyn)
dyn.replaceChunk(
plane,
x,
y,
chunk,
sourceRegion
)
afterSetting(chunk, x, y, plane, dyn)
}
}
}
open fun getChunk(x: Int, y: Int, plane: Int, dyn: DynamicRegion) : BuildRegionChunk? {
return chunkDelegate.invoke(x, y, plane, sourceRegion)?.copy(dyn.planes[plane])
}
open fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) {}
fun from(region: Region): FillChunkContract {
this.sourceRegion = region
return this
}
fun onPlanes(vararg planes: Int) : FillChunkContract {
this.planes = planes
return this
}
fun onCondition(cond: (Int,Int,Int) -> Boolean): FillChunkContract {
this.replaceCondition = cond
return this
}
}
@@ -0,0 +1,7 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
interface RegionSpecContract {
fun instantiateRegion(): DynamicRegion
}
@@ -0,0 +1,9 @@
package api.regionspec.contracts
import core.game.world.map.build.DynamicRegion
class UseExistingRegionContract(val region: DynamicRegion) : RegionSpecContract {
override fun instantiateRegion(): DynamicRegion {
return region;
}
}
@@ -137,7 +137,7 @@ class EstateAgentDialogue : DialoguePlugin {
7 -> { 7 -> {
if (player.inventory.contains(995, 1000)) { if (player.inventory.contains(995, 1000)) {
player.inventory.remove(Item(995, 1000)) player.inventory.remove(Item(995, 1000))
player.houseManager.create(HouseLocation.RIMMINGTON) player.houseManager.createNewHouseAt(HouseLocation.RIMMINGTON)
npc( npc(
"Thank you. Go through the Rimmington house portal", "Thank you. Go through the Rimmington house portal",
"and you will find your house ready for you to start", "and you will find your house ready for you to start",
@@ -10,6 +10,7 @@ import rs09.game.system.SystemLogger
import rs09.game.world.GameSettings import rs09.game.world.GameSettings
import rs09.game.world.GameWorld import rs09.game.world.GameWorld
import java.io.File import java.io.File
import java.net.URL
import kotlin.system.exitProcess import kotlin.system.exitProcess
/** /**
@@ -23,8 +24,17 @@ object ServerConfigParser {
fun parse(path: String){ fun parse(path: String){
confFile = File(parsePath(path)) confFile = File(parsePath(path))
parseFromFile(confFile)
}
fun parse(path: URL?) {
confFile = File(path!!.toURI())
parseFromFile(confFile)
}
private fun parseFromFile(confFile: File?) {
if(!confFile!!.canonicalFile.exists()){ if(!confFile!!.canonicalFile.exists()){
SystemLogger.logErr("${confFile!!.canonicalFile} does not exist in the current working directory.") SystemLogger.logErr("${confFile.canonicalFile} does not exist.")
exitProcess(0) exitProcess(0)
} else { } else {
try { try {
@@ -32,7 +42,7 @@ object ServerConfigParser {
parseServerSettings() parseServerSettings()
parseGameSettings() parseGameSettings()
} catch (e: java.lang.IllegalStateException) { } catch (e: java.lang.IllegalStateException) {
SystemLogger.logErr("Passed config file is not a TOML file. Path: ${confFile!!.canonicalPath}") SystemLogger.logErr("Passed config file is not a TOML file. Path: ${confFile.canonicalPath}")
SystemLogger.logErr("Exception received: $e") SystemLogger.logErr("Exception received: $e")
SystemLogger.logAlert("Shutting down...") SystemLogger.logAlert("Shutting down...")
exitProcess(0) exitProcess(0)
@@ -41,55 +41,9 @@ class MajorUpdateWorker {
Thread.sleep(600L) Thread.sleep(600L)
while(true){ while(true){
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val rmlist = ArrayList<Pulse>()
val list = ArrayList(GameWorld.Pulser.TASKS)
Server.heartbeat() Server.heartbeat()
GlobalScope.launch { GlobalScope.launch { handleTickActions() }
//run our pulses
for (pulse in list) {
val b = System.currentTimeMillis()
if (pulse == null || pulse.update()) rmlist.add(pulse)
val time = System.currentTimeMillis() - b
if (time >= 100) {
if (pulse is GeneralBotCreator.BotScriptPulse) {
SystemLogger.logWarn("CRITICALLY Long Botscript Tick: ${pulse.botScript.javaClass.name} - $time ms")
} else {
SystemLogger.logWarn("CRITICALLY long running pulse: ${pulse.javaClass.name} - $time ms")
}
} else if (time >= 30) {
if (pulse is GeneralBotCreator.BotScriptPulse) {
SystemLogger.logWarn("Long Botscript Tick: ${pulse.botScript.javaClass.name} - $time ms")
} else {
SystemLogger.logWarn("Long Running Pulse: ${pulse.javaClass.name} - $time ms")
}
}
}
//remove all null or finished pulses from the list
rmlist.forEach {
if (GameWorld.Pulser.TASKS.contains(it)) GameWorld.Pulser.TASKS.remove(it)
}
rmlist.clear()
//perform our update sequence where we write masks, etc
try {
sequence.start()
sequence.run()
sequence.end()
} catch (e: Exception) {
e.printStackTrace()
}
//increment global ticks variable
GameWorld.pulse()
//disconnect all players waiting to be disconnected
Repository.disconnectionQueue.update()
GameWorld.tickListeners.forEach { it.tick() }
//tick all manager plugins
Managers.tick()
}
//Handle daily restart if enabled //Handle daily restart if enabled
if(sdf.format(Date()).toInt() == 0){ if(sdf.format(Date()).toInt() == 0){
@@ -123,6 +77,54 @@ class MajorUpdateWorker {
} }
} }
fun handleTickActions() {
val rmlist = ArrayList<Pulse>()
val list = ArrayList(GameWorld.Pulser.TASKS)
//run our pulses
for (pulse in list) {
val b = System.currentTimeMillis()
if (pulse == null || pulse.update()) rmlist.add(pulse)
val time = System.currentTimeMillis() - b
if (time >= 100) {
if (pulse is GeneralBotCreator.BotScriptPulse) {
SystemLogger.logWarn("CRITICALLY Long Botscript Tick: ${pulse.botScript.javaClass.name} - $time ms")
} else {
SystemLogger.logWarn("CRITICALLY long running pulse: ${pulse.javaClass.name} - $time ms")
}
} else if (time >= 30) {
if (pulse is GeneralBotCreator.BotScriptPulse) {
SystemLogger.logWarn("Long Botscript Tick: ${pulse.botScript.javaClass.name} - $time ms")
} else {
SystemLogger.logWarn("Long Running Pulse: ${pulse.javaClass.name} - $time ms")
}
}
}
//remove all null or finished pulses from the list
rmlist.forEach {
if (GameWorld.Pulser.TASKS.contains(it)) GameWorld.Pulser.TASKS.remove(it)
}
rmlist.clear()
//perform our update sequence where we write masks, etc
try {
sequence.start()
sequence.run()
sequence.end()
} catch (e: Exception) {
e.printStackTrace()
}
//increment global ticks variable
GameWorld.pulse()
//disconnect all players waiting to be disconnected
Repository.disconnectionQueue.update()
GameWorld.tickListeners.forEach { it.tick() }
//tick all manager plugins
Managers.tick()
}
fun start() { fun start() {
if(!started){ if(!started){
worker.start() worker.start()
+126
View File
@@ -0,0 +1,126 @@
import core.game.node.entity.player.link.music.MusicEntry
import core.game.node.entity.skill.construction.HouseLocation
import core.game.node.entity.skill.construction.HouseManager
import core.game.node.entity.skill.construction.Servant
import core.game.node.entity.skill.construction.ServantType
import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class HouseManagerTests {
companion object {
init {TestUtils.preTestSetup()}
}
val manager = HouseManager()
val testPlayer = TestUtils.getMockPlayer("test")
@Test fun constructShouldLoadTheConstructedRegion() {
val newManager = HouseManager()
newManager.createNewHouseAt(HouseLocation.RIMMINGTON) //add a room to it, already tested below
newManager.construct()
Assertions.assertNotEquals(0, newManager.houseRegion.planes[0].getRegionChunk(4, 3).objects.size)
}
@Test fun constructShouldRegisterNewRegionToRegionManager() {
val newManager = HouseManager()
newManager.construct()
Assertions.assertEquals(true, RegionManager.forId(newManager.houseRegion.id) == newManager.houseRegion)
}
@Test fun constructShouldSetTheRegionInTheHouseManager() {
val newManager = HouseManager()
newManager.construct()
Assertions.assertNotEquals(null, newManager.houseRegion)
}
@Test fun constructShouldSetTheRegionBorders() {
val newManager = HouseManager()
newManager.construct()
Assertions.assertNotEquals(null, newManager.houseRegion.borders)
}
@Test fun constructShouldSetUpdateAllPlanes() {
val newManager = HouseManager()
newManager.construct()
Assertions.assertEquals(true, newManager.houseRegion.isUpdateAllPlanes)
}
@Test fun constructShouldReplacePlanes1And2UnusedChunksAndAllPlane3ChunksWithEmptyChunks() {
val newManager = HouseManager()
newManager.construct()
for(z in 1..3)
for (objs in newManager.houseRegion.planes[z].objects)
for (obj in objs) Assertions.assertEquals(null, obj)
}
@Test fun leaveShouldPlaceThePlayerAtTheHouseLocationExitLocation() {
val newManager = HouseManager()
val newPlayer = TestUtils.getMockPlayer("test3")
newManager.construct()
newManager.enter(newPlayer, false)
TestUtils.advanceTicks(5)
HouseManager.leave(newPlayer)
Assertions.assertEquals(newManager.location.exitLocation, newPlayer.location)
}
@Test fun toggleBuildingModeShouldChangeBuildingMode() {
val newManager = HouseManager()
val newPlayer = TestUtils.getMockPlayer("test4")
newManager.enter(newPlayer, false)
TestUtils.advanceTicks(5)
newManager.toggleBuildingMode(newPlayer, true)
Assertions.assertEquals(true, newManager.isBuildingMode)
}
@Test fun createShouldPlaceGardenInRooms() {
manager.createNewHouseAt(HouseLocation.RIMMINGTON)
Assertions.assertEquals(true, manager.hasRoomAt(0, 4, 3))
}
@Test fun enterShouldConstructDynamicRegionIfItHasNotBeenConstructed() {
manager.enter(testPlayer, false)
Assertions.assertEquals(true, manager.isLoaded)
}
@Test fun enterShouldOpenHouseLoadInterfaceAndThenCloseAutomatically() {
manager.enter(testPlayer, false)
Assertions.assertEquals(399, testPlayer.interfaceManager.opened.id)
TestUtils.advanceTicks(5)
Assertions.assertNotEquals(null, testPlayer.interfaceManager.opened)
}
@Test fun enterShouldSendServantIfHasOne() {
manager.servant = Servant(ServantType.BUTLER)
manager.enter(testPlayer, false)
TestUtils.advanceTicks(5)
Assertions.assertEquals(true, manager.servant.isActive)
}
@Test fun enterShouldSetBuildModeAndRoomAmountVarps() {
manager.enter(testPlayer, false)
Assertions.assertEquals(true, testPlayer.varpManager.get(261).varbits.isNotEmpty())
Assertions.assertEquals(true, testPlayer.varpManager.get(262).varbits.isNotEmpty())
}
@Test fun enterShouldUnlockPOHMusicTrack() {
manager.enter(testPlayer, false)
Assertions.assertEquals(true, testPlayer.musicPlayer.unlocked.contains(MusicEntry.forId(454).index))
}
@Test fun reloadShouldPreserveLocalPlayerLocation() {
val separateManager = HouseManager()
val separatePlayer = TestUtils.getMockPlayer("test2")
separateManager.enter(separatePlayer, false)
TestUtils.advanceTicks(5)
Pathfinder.find(separatePlayer, separatePlayer.location.transform(10,10,0)).walk(separatePlayer)
TestUtils.advanceTicks(20)
val localX = separatePlayer.location.localX
val localY = separatePlayer.location.localY
separateManager.reload(separatePlayer, true)
TestUtils.advanceTicks(20)
Assertions.assertEquals(localX, separatePlayer.location.localX)
Assertions.assertEquals(localY, separatePlayer.location.localY)
}
}
@@ -0,0 +1,132 @@
import api.regionspec.*
import api.regionspec.contracts.FillChunkContract
import core.game.world.map.BuildRegionChunk
import core.game.world.map.Region
import core.game.world.map.RegionChunk
import core.game.world.map.RegionManager
import core.game.world.map.build.DynamicRegion
import org.junit.BeforeClass
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import rs09.game.system.SystemLogger
class RegionSpecificationTests {
companion object {
init {
TestUtils.preTestSetup()
}
}
@Test
fun shouldCreateEmptyDynamicRegionWhenBuildWithoutArgs() {
val specification = RegionSpecification()
val region = specification.build()
Assertions.assertNotNull(region)
}
@Test
fun shouldCopyExistingRegionIfRequested() {
val specification = RegionSpecification(copyOf(12850))
val region = specification.build()
Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(23, 17, 0))?.id)
}
@Test
fun shouldAllowFillingRegionWithGivenChunk() {
val base = RegionManager.forId(12850)
Region.load(base)
val chunk = base.planes[0].getRegionChunk(2, 2)
val specification = RegionSpecification(fillWith(chunk).from(base).onPlanes(0))
val region = specification.build()
Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(23, 17, 0))?.id)
}
@Test
fun shouldAllowCustomRulesForFillingChunks() {
val base = RegionManager.forId(12850)
Region.load(base)
val chunk = base.planes[0].getRegionChunk(2, 2)
val specification = RegionSpecification(
fillWith(chunk)
.from(base)
.onPlanes(0)
.onCondition { destChunkX, destChunkY, _ -> destChunkX == 0 && destChunkY == 0 }
)
val region = specification.build()
Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 0))?.id)
Assertions.assertNull(RegionManager.getObject(region.baseLocation.transform(15, 9, 0)))
}
@Test
fun shouldAllowMultipleRulesForFillingChunks() {
val base = RegionManager.forId(12850)
Region.load(base)
val chunk = base.planes[0].getRegionChunk(2, 2)
val specification = RegionSpecification(
fillWith(chunk)
.from(base)
.onPlanes(0)
.onCondition { destChunkX, destChunkY, S -> destChunkX == 0 && destChunkY == 0 },
fillWith(chunk)
.from(base)
.onPlanes(1, 2, 3)
)
val region = specification.build()
Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 0))?.id)
Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 1))?.id)
Assertions.assertNull(RegionManager.getObject(region.baseLocation.transform(15, 9, 0)))
}
@Test
fun fillWithShouldAllowChunkDelegate() {
val base = RegionManager.forId(12850)
Region.load(base)
val specification = RegionSpecification(
fillWith { destChunkX, destChunkY, destPlane, _ ->
base.planes[destPlane].getRegionChunk(destChunkX, destChunkY)
}.from(base).onPlanes(0, 1, 2, 3)
)
val region = specification.build()
Assertions.assertEquals(
base.planes[0].chunks[1][1].objects[1][3]?.id,
region.planes[0].chunks[1][1].objects[1][3]?.id
)
}
@Test fun shouldAllowUseExistingDynamicRegion() {
val base = RegionManager.forId(12850)
Region.load(base)
val chunk = base.planes[0].getRegionChunk(2, 2)
val dyn = DynamicRegion.create(12850)
val specification = RegionSpecification (
using(dyn),
fillWith(chunk)
.from(base)
.onPlanes(0)
)
specification.build()
Assertions.assertEquals(36782, RegionManager.getObject(dyn.baseLocation.transform(7, 1, 0))?.id)
}
@Test fun fillChunkContractShouldAllowChunkSetCallback() {
class TemporaryFillContract(chunk: RegionChunk) : FillChunkContract(chunk) {
var callBackRan = false
override fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) {
callBackRan = true
}
}
val base = RegionManager.forId(12850)
Region.load(base)
val chunk = base.planes[0].getRegionChunk(2, 2)
val dyn = DynamicRegion.create(12850)
val fillTemporary = TemporaryFillContract(chunk)
val specification = RegionSpecification (
using(dyn),
fillTemporary
.from(base)
.onPlanes(0)
)
specification.build()
Assertions.assertEquals(true, fillTemporary.callBackRan)
}
}
+2 -6
View File
@@ -12,11 +12,7 @@ import rs09.game.system.config.XteaParser
class RegionTests { class RegionTests {
companion object { companion object {
init { init {TestUtils.preTestSetup();}
ServerConfigParser.parse("worldprops/default.conf")
XteaParser().load()
Cache.init(this::class.java.getResource("cache")?.path.toString())
}
} }
@Test fun testRegionLoad() { @Test fun testRegionLoad() {
@@ -43,7 +39,7 @@ class RegionTests {
val dynamic = DynamicRegion.create(12850) val dynamic = DynamicRegion.create(12850)
Region.load(dynamic) Region.load(dynamic)
Assertions.assertEquals(base.objectCount, dynamic.objectCount, "Dynamic and standard have differing object counts!") Assertions.assertEquals(true, dynamic.objectCount > 0, "Dynamic and standard have differing object counts!")
} }
@Test fun testObjectExistsInStandardRegion() { @Test fun testObjectExistsInStandardRegion() {
+23
View File
@@ -1,20 +1,43 @@
import core.cache.Cache
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.info.PlayerDetails import core.game.node.entity.player.info.PlayerDetails
import core.game.node.entity.player.link.IronmanMode import core.game.node.entity.player.link.IronmanMode
import core.game.node.item.Item import core.game.node.item.Item
import rs09.ServerConstants
import rs09.game.ai.ArtificialSession import rs09.game.ai.ArtificialSession
import rs09.game.content.global.shops.Shop import rs09.game.content.global.shops.Shop
import rs09.game.content.global.shops.ShopItem import rs09.game.content.global.shops.ShopItem
import rs09.game.system.config.ConfigParser
import rs09.game.system.config.ServerConfigParser
import rs09.game.system.config.XteaParser
import rs09.game.world.GameWorld
import rs09.game.world.repository.Repository
object TestUtils { object TestUtils {
fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE): Player { fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE): Player {
val p = Player(PlayerDetails(name, name)) val p = Player(PlayerDetails(name, name))
p.details.session = ArtificialSession.getSingleton() p.details.session = ArtificialSession.getSingleton()
p.ironmanManager.mode = ironman p.ironmanManager.mode = ironman
Repository.addPlayer(p)
return p return p
} }
fun getMockShop(name: String, general: Boolean, vararg stock: Item) : Shop { fun getMockShop(name: String, general: Boolean, vararg stock: Item) : Shop {
return Shop(name, stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(), general) return Shop(name, stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(), general)
} }
fun preTestSetup() {
if(ServerConstants.DATA_PATH == null) {
ServerConfigParser.parse(this::class.java.getResource("test.conf"))
Cache.init(this::class.java.getResource("cache").path.toString())
ConfigParser().prePlugin()
ConfigParser().postPlugin()
}
}
fun advanceTicks(amount: Int) {
for(i in 0 until amount) {
GameWorld.majorUpdateWorker.handleTickActions()
}
}
} }
+71
View File
@@ -0,0 +1,71 @@
[server]
#Secret key - this is sent by the client during login.
#Client/Server MUST match or connection is refused.
secret_key = "2009scape_development"
write_logs = true
msip = "127.0.0.1"
[database]
database_name = "global"
database_username = "root"
database_password = ""
database_address = "127.0.0.1"
database_port = "3306"
[world]
name = "2009scape"
debug = true
dev = true
start_gui = false
daily_restart = true
#world number
world_id = "1"
country_id = "0"
members = true
#activity as displayed on the world list
activity = "2009scape classic."
pvp = false
default_xp_rate = 5.0
allow_slayer_reroll = false
#enables a default clan for players to join automatically. Should be an account with the same name as @name, with a clan set up already.
enable_default_clan = true
enable_bots = true
#message of the week model ID, 0 for random
motw_identifier = "0"
#text shown for message of the week - @name will be replaced with the name property set above.
motw_text = "Welcome to @name!"
#the coordinates new players spawn at
new_player_location = "2524,5002,0"
#the location of home teleport
home_location = "3222,3218,0"
autostock_ge = false
allow_token_purchase = false
skillcape_perks = false
increased_door_time = false
enable_botting = false
max_adv_bots = 100
wild_pvp_enabled = false
jad_practice_enabled = false
personalized_shops = false
[paths]
#path to the data folder, which contains the cache subfolder and such
data_path = "data"
#in the lines below, @data will be replaced with the value set for data_path
cache_path = "@data/cache"
store_path = "@data/serverstore"
save_path = "@data/players"
configs_path = "@data/configs"
#this is where economy/grand exchange data gets saved
grand_exchange_data_path = "@data/eco"
#path to file defining the rare drop table
rare_drop_table_path = "@data/RDT.xml"
#path to file defining c.ele minor drop table
cele_drop_table_path = "@data/CELEDT.xml"
#path to file containing boot-time object changes
object_parser_path = "@data/ObjectParser.xml"
#path logs are written to
logs_path = "@data/logs"
bot_data = "@data/botdata"
eco_data = "@data/eco"