New server config option to preload the world map on boot, removes lag caused by lazy loading (server.preload_map = true)

Cached region clipping flags for faster lookups
Minor optimizations to setAttribute codepath which avoids unnecessary checks
Fixed a bug that would allow entities to attack through walls
Fixed two separate bugs that would cause odd behavior attacking large entities from certain angles
Removed unnecessary schema from global.sql
Ensured that all but the `members` and `worlds` tables could be safely dropped out of the DB
Better automatic handling for creating the default server account and creating the default server clan
Fixed bugs related to interfaces flagged as uncloseable that would cause stack overflows
Fixed premature home teleport while in tutorial
Fixed getting stuck by going back up ladder in tutorial
Fixed bug that prevented bots from taking damage
Fixed a bug where players would not be in the default clan after completion of the tutorial
Green Dragon bots reintroduced, no longer buy any items (these bots sell loot from green dragons in the wilderness on the GE)
This commit is contained in:
Ceikry 2022-06-24 14:40:26 +00:00 committed by Ryan
parent 67e54d5429
commit ebf2ed4ab4
32 changed files with 495 additions and 616 deletions

File diff suppressed because one or more lines are too long

View file

@ -125,14 +125,11 @@ public class Component {
* @param c The component.
*/
public static void setUnclosable(Player p, Component c) {
p.setAttribute("close_c_", false);
p.setAttribute("close_c_", true);
c.setCloseEvent(new CloseEvent() {
@Override
public boolean close(Player player, Component c) {
if (!player.getAttribute("close_c_", false)) {
return false;
}
return true;
return !player.getAttribute("close_c_", false);
}
});
}

View file

@ -20,6 +20,9 @@ import core.game.node.entity.skill.Skills;
import core.game.node.entity.state.EntityState;
import core.game.node.entity.state.StateManager;
import core.game.system.task.Pulse;
import core.game.world.map.zone.ZoneBorders;
import org.jetbrains.annotations.NotNull;
import rs09.game.system.SystemLogger;
import rs09.game.world.GameWorld;
import core.game.world.map.Location;
import core.game.world.map.Viewport;
@ -33,6 +36,7 @@ import rs09.game.world.update.UpdateMasks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -780,10 +784,6 @@ public abstract class Entity extends Node {
attributes.setAttribute(key, value);
}
public void setExpirableAttribute(String key, Object value, Long timeToLive){
attributes.setExpirableAttribute(key,value,timeToLive);
}
/**
* Gets an attribute.
* @param key The attribute name.
@ -938,4 +938,34 @@ public abstract class Entity extends Node {
this.invisible = invisible;
}
public Location getClosestOccupiedTile(@NotNull Location other) {
List<Location> occupied = getOccupiedTiles();
Location closest = location;
if (occupied.size() > 1) {
double lowest = 9999;
for (Location tile : occupied) {
double dist = tile.getDistance(other);
if (dist < lowest) {
lowest = dist;
closest = tile;
}
}
}
return closest;
}
public List<Location> getOccupiedTiles() {
ArrayList<Location> occupied = new ArrayList<>();
Location northEast = location.transform(size, size, 0);
for (int x = location.getX(); x < northEast.getX(); x++) {
for (int y = location.getY(); y < northEast.getY(); y++) {
occupied.add(Location.create(x, y, location.getZ()));
}
}
return occupied;
}
}

View file

@ -11,6 +11,7 @@ import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.prayer.PrayerType;
import core.game.node.item.Item;
import core.game.system.task.Pulse;
import rs09.game.ai.AIPlayer;
import rs09.game.world.GameWorld;
import core.game.world.map.zone.ZoneType;
@ -144,7 +145,7 @@ public final class ImpactHandler {
if (disabledTicks > GameWorld.getTicks()) {
return null;
}
if (entity instanceof Player && !(entity.getAttribute("tutorial:complete",false))) {
if (entity instanceof Player && !(entity instanceof AIPlayer) && !(entity.getAttribute("tutorial:complete",false))) {
Impact impact = new Impact(source, 0, style, HitsplatType.MISS);
impactQueue.add(impact);
return impact;

View file

@ -136,7 +136,7 @@ public final class GameAttributes {
*/
public void setAttribute(String key, Object value) {
if (key.startsWith("/save:")) {
key = key.substring(6, key.length());
key = key.substring(6);
if (!savedAttributes.contains(key)) {
savedAttributes.add(key);
}
@ -144,17 +144,6 @@ public final class GameAttributes {
attributes.put(key, value);
}
/**
* Sets an inherently temporary (but saved cross-session) key.
* @param key the key to set
* @param value the value to assign to the key
* @param timeToLive the time (in milliseconds) that the key will be valid for
*/
public void setExpirableAttribute(String key, Object value, Long timeToLive){
setAttribute(key,value);
keyExpirations.put(key,System.currentTimeMillis() + timeToLive);
}
/**
* Gets an attribute.
* @param key The attribute name.
@ -163,9 +152,6 @@ public final class GameAttributes {
@SuppressWarnings("unchecked")
public <T> T getAttribute(String key) {
key = key.replace("/save:","");
if (!attributes.containsKey(key)) {
return null;
}
return (T) attributes.get(key);
}
@ -182,9 +168,6 @@ public final class GameAttributes {
if (object != null) {
return (T) object;
}
if(keyExpirations.containsKey(string) && keyExpirations.get(string) < System.currentTimeMillis()){
return fail;
}
return fail;
}

View file

@ -432,6 +432,7 @@ public class NPC extends Entity {
if (dialoguePlayer == null || !dialoguePlayer.isActive() || !dialoguePlayer.getInterfaceManager().hasChatbox()) {
dialoguePlayer = null;
if (walks && !getPulseManager().hasPulseRunning() && !getProperties().getCombatPulse().isAttacking() && !getProperties().getCombatPulse().isInCombat() && nextWalk < GameWorld.getTicks()) {
if (RandomFunction.nextBool()) return;
setNextWalk();
Location l = getMovementDestination();
if (canMove(l)) {

View file

@ -193,9 +193,6 @@ public class RevenantNPC extends AbstractNPC {
@Override
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
if (entity.asPlayer().isArtificial()) {
return false;
}
if (entity instanceof Player) {
if (!checkCombatLevel(entity.asPlayer()) && !entity.asPlayer().isAdmin()) {
if(message) {

View file

@ -90,7 +90,7 @@ public class BuildRegionChunk extends RegionChunk {
objects[i][x][y] = null;
}
plane.getObjects()[baseX + x][baseY + y] = null;
plane.getFlags().getClippingFlags()[baseX + x][baseY + y] = 0;
plane.getFlags().clearFlag(baseX + x, baseY + y);
}
}
clear();

View file

@ -5,8 +5,11 @@ import core.game.node.Node;
import core.game.world.map.path.Path;
import core.game.world.map.path.Pathfinder;
import core.tools.RandomFunction;
import org.jetbrains.annotations.NotNull;
import rs09.game.system.SystemLogger;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a location on the world map.
@ -491,4 +494,45 @@ public final class Location extends Node {
public void setZ(int z) {
this.z = z;
}
@NotNull
public List<Location> getStepComponents(Direction dir) {
List<Location> output = new ArrayList<>(2);
int stepX = dir.getStepX();
int stepY = dir.getStepY();
if (stepX != 0) output.add(transform(stepX, 0, 0));
if (stepY != 0) output.add(transform(0, stepY, 0));
return output;
}
public Direction deriveDirection(Location location) {
int diffX = location.x - this.x;
int diffY = location.y - this.y;
diffX = diffX >= 0 ? Math.min(diffX, 1) : -1;
diffY = diffY >= 0 ? Math.min(diffY, 1) : -1;
StringBuilder sb = new StringBuilder();
if (diffY != 0) {
if (diffY > 0) {
sb.append("NORTH");
} else {
sb.append("SOUTH");
}
}
if (diffX != 0) {
if (sb.length() > 0) sb.append("_");
if (diffX > 0) {
sb.append("EAST");
} else {
sb.append("WEST");
}
}
if (sb.length() == 0) return null;
return Direction.valueOf(sb.toString());
}
}

View file

@ -301,8 +301,8 @@ public class Region {
byte[][][] mapscapeData = new byte[4][SIZE][SIZE];
for (RegionPlane plane : r.planes) {
plane.getFlags().setLandscape(new boolean[SIZE][SIZE]);
plane.getFlags().setClippingFlags(new int[SIZE][SIZE]);
plane.getProjectileFlags().setClippingFlags(new int[SIZE][SIZE]);
//plane.getFlags().setClippingFlags(new int[SIZE][SIZE]);
//plane.getProjectileFlags().setClippingFlags(new int[SIZE][SIZE]);
}
if (mapscapeId > -1) {
ByteBuffer mapscape = ByteBuffer.wrap(Cache.getIndexes()[5].getCacheFile().getContainerUnpackedData(mapscapeId));

View file

@ -194,7 +194,7 @@ public class RegionChunk {
copy[x][y] = objects[x][y];
staticCopy[x][y] = plane.getObjects()[baseX + x][baseY + y];
objects[x][y] = plane.getObjects()[baseX + x][baseY + y] = null;
plane.getFlags().getClippingFlags()[baseX + x][baseY + y] = 0;
plane.getFlags().clearFlag(baseX + x, baseY + y);
}
}
rotation = direction.toInteger();

View file

@ -11,6 +11,7 @@ import rs09.game.system.SystemLogger
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.HashMap
/**
* Manages the regions.
@ -21,6 +22,8 @@ object RegionManager {
* The region cache mapping.
*/
private val REGION_CACHE: MutableMap<Int, Region> = HashMap()
@JvmStatic val CLIPPING_FLAGS = HashMap<Int, Array<Int>>()
@JvmStatic val PROJECTILE_FLAGS = HashMap<Int, Array<Int>>()
public val LOCK = ReentrantLock()
@ -80,16 +83,50 @@ object RegionManager {
*/
@JvmStatic
fun getClippingFlag(z: Int, x: Int, y: Int): Int {
var x = x
var y = y
val region = forId(((x shr 6) shl 8) or (y shr 6))
Region.load(region)
if (!region.isHasFlags) {
return -1
val regionX = x shr 6
val regionY = y shr 6
val localX = x and 63
val localY = y and 63
return getClippingFlag(z, regionX, regionY, localX, localY)
}
/**
* Gets the clipping flags using Jagex-style coords
* e.g 0_50_50_13_13 gets plane 0, region 50-50 (12850), (13, 13) which is in lumbridge.
*/
@JvmStatic
fun getClippingFlag(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int, projectile: Boolean = false) : Int {
val (region, index) = getFlagIndex(z, regionX, regionY, localX, localY)
var flag = getFlags(region, projectile)[index]
if (flag == -1) {
val r = forId((regionX shr 8) or regionY)
if (!r.isLoaded)
Region.load(r)
if (!r.isHasFlags)
return -1
flag = getFlags(region, projectile)[index]
}
x -= (x shr 6) shl 6
y -= (y shr 6) shl 6
return region.planes[z].flags.clippingFlags[x][y]
return flag
}
private fun getFlagIndex(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int) : Pair<Int,Int> {
return Pair((regionX shl 8) or regionY, (z * 64 * 64) + (localX * 64) + localY)
}
@JvmStatic
fun getFlags(regionX: Int, regionY: Int, projectile: Boolean) : Array<Int> {
val region = (regionX shl 8) or regionY
return getFlags(region, projectile)
}
@JvmStatic
fun getFlags(regionId: Int, projectile: Boolean) : Array<Int> {
return if (projectile)
PROJECTILE_FLAGS.getOrPut (regionId) {Array(16384){0}}
else
CLIPPING_FLAGS.getOrPut (regionId) {Array(16384){-1}}
}
/**
@ -136,26 +173,6 @@ object RegionManager {
return region.planes[z].flags.landscape[x][y]
}
/**
* Sets the clipping flag on the given location.
* @param l The location.
* @param flag The flag to set.
*/
@JvmStatic
fun setClippingFlag(l: Location, flag: Int) {
var x = l.x
var y = l.y
val z = l.z
val region = forId(((x shr 6) shl 8) or (y shr 6))
Region.load(region)
if (!region.isHasFlags) {
return
}
x -= x shr 6 shl 6
y -= y shr 6 shl 6
region.planes[z].flags.clippingFlags[x][y] = flag
}
/**
* Adds a clipping flag.
* @param z The plane.
@ -217,16 +234,11 @@ object RegionManager {
*/
@JvmStatic
fun getProjectileFlag(z: Int, x: Int, y: Int): Int {
var x = x
var y = y
val region = forId(((x shr 6) shl 8) or (y shr 6))
Region.load(region)
if (!region.isHasFlags) {
return -1
}
x -= (x shr 6) shl 6
y -= (y shr 6) shl 6
return region.planes[z].projectileFlags.clippingFlags[x][y]
val regionX = x shr 6
val regionY = y shr 6
val localX = x and 63
val localY = y and 63
return getClippingFlag(z, regionX, regionY, localX, localY, true)
}
/**

View file

@ -320,8 +320,8 @@ public final class DynamicRegion extends Region {
if (chunk == null) {
for (int i = x << 3; i < (x + 1) << 3; i++) {
for (int j = y << 3; j < (y + 1) << 3; j++) {
p.getFlags().getClippingFlags()[i][j] = -1;
p.getProjectileFlags().getClippingFlags()[i][j] = -1;
p.getFlags().invalidateFlag(i, j);
p.getProjectileFlags().invalidateFlag(i, j);
Scenery object = p.getObjects()[i][j];
if (object != null) {
LandscapeParser.removeScenery(object);
@ -344,8 +344,8 @@ public final class DynamicRegion extends Region {
int fromX = (l.getX() - regionBase.getX()) + i;
int fromY = (l.getY() - regionBase.getY()) + j;
p.getFlags().getLandscape()[toX][toY] = rp.getFlags().getLandscape()[fromX][fromY];
p.getFlags().getClippingFlags()[toX][toY] = rp.getFlags().getClippingFlags()[fromX][fromY];
p.getProjectileFlags().getClippingFlags()[toX][toY] = rp.getProjectileFlags().getClippingFlags()[fromX][fromY];
p.getFlags().flag(fromX, fromY, rp.getFlags().getFlag(fromX, fromY));
p.getProjectileFlags().flag(fromX, fromY, rp.getFlags().getFlag(fromX, fromY));
Scenery[] objects = { rp.getChunkObject(fromX, fromY) };
RegionChunk ch = rp.getChunks()[fromX >> 3][fromY >> 3];
if (ch instanceof BuildRegionChunk) {

View file

@ -60,6 +60,7 @@ public final class MapscapeParser {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
r.getPlanes()[z].getFlags().flagEmptyTile(x,y);
if ((mapscape[z][x][y] & 0x1) == 1) {
int plane = z;
if ((mapscape[1][x][y] & 0x2) == 2) {

View file

@ -1,6 +1,10 @@
package core.game.world.map.build;
import core.game.world.map.RegionManager;
import kotlin.Pair;
import rs09.game.system.SystemLogger;
import static java.lang.Math.max;
/**
* Holds a region's flags like clipping flags, members, ...
@ -9,6 +13,11 @@ import core.game.world.map.RegionManager;
*/
public final class RegionFlags {
public static final int TILE_OBJECT = 0x40000;
public static final int EMPTY_TILE = 0;
public static final int SOLID_TILE = 0x200000;
public static final int OBJ_10_PROJECTILE = 0x20000;
public static final int OBJ_10 = 0x100;
/**
* The plane.
*/
@ -28,11 +37,6 @@ public final class RegionFlags {
* The base y-coordinate.
*/
private final int baseY;
/**
* The clipping flags.
*/
private int[][] clippingFlags;
/**
* The landscape data.
@ -42,7 +46,7 @@ public final class RegionFlags {
/**
* If the flags are set for projectile clipping
*/
private boolean projectile;
private final boolean projectile;
/**
* Constructs a new {@code RegionFlags} {@code Object}.
@ -71,7 +75,11 @@ public final class RegionFlags {
* @param y The y-coordinate.
*/
public void flagSolidTile(int x, int y) {
clippingFlags[x][y] |= 0x200000;
flag(x, y, SOLID_TILE);
}
public void flagEmptyTile(int x, int y) {
flag(x, y, EMPTY_TILE);
}
/**
@ -80,7 +88,7 @@ public final class RegionFlags {
* @param y The y-coordinate.
*/
public void flagTileObject(int x, int y) {
clippingFlags[x][y] |= 0x40000;
flag(x, y, TILE_OBJECT);
}
/**
@ -89,12 +97,7 @@ public final class RegionFlags {
* @param y The y-coordinate.
*/
public void unflagTileObject(int x, int y) {
if (clippingFlags == null) {
return;
}
if ((clippingFlags[x][y] & 0x40000) != 0) {
clippingFlags[x][y] &= ~0x40000;
}
unflag(x, y, TILE_OBJECT);
}
/**
@ -106,9 +109,9 @@ public final class RegionFlags {
* @param projectileClipped If the object is solid.
*/
public void flagSolidObject(int x, int y, int sizeX, int sizeY, boolean projectileClipped) {
int clipdata = 0x100;
int clipdata = OBJ_10;
if (projectileClipped) {
clipdata += 0x20000;
clipdata += OBJ_10_PROJECTILE;
}
for (int i = x; i < x + sizeX; i++) {
for (int j = y; j < y + sizeY; j++) {
@ -125,12 +128,11 @@ public final class RegionFlags {
*/
public void flag(int x, int y, int clipdata) {
if (x > -1 && x < 64 && y > -1 && y < 64) {
clippingFlags[x][y] |= clipdata;
addFlag(x, y, clipdata);
} else {
RegionManager.addClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata);
}
}
/**
* Unflags a solid object (type 10/11).
@ -141,9 +143,9 @@ public final class RegionFlags {
* @param projectileClipped If the object is solid.
*/
public void unflagSolidObject(int x, int y, int sizeX, int sizeY, boolean projectileClipped) {
int clipdata = 0x100;
int clipdata = OBJ_10;
if (projectileClipped) {
clipdata += 0x20000;
clipdata += OBJ_10_PROJECTILE;
}
for (int i = x; i < x + sizeX; i++) {
for (int j = y; j < y + sizeY; j++) {
@ -159,18 +161,47 @@ public final class RegionFlags {
* @param clipdata The clip data.
*/
public void unflag(int x, int y, int clipdata) {
if (clippingFlags == null) {
return;
}
if (x > -1 && x < 64 && y > -1 && y < 64) {
if ((clippingFlags[x][y] & clipdata) != 0) {
clippingFlags[x][y] &= ~clipdata;
}
removeFlag(x, y, clipdata);
} else {
RegionManager.removeClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata);
}
}
private Pair<Integer, Integer> getFlagIndex(int x, int y) {
return new Pair<>(((baseX >> 6) << 8) | (baseY >> 6), (plane * 64 * 64) + (x * 64) + y);
}
public int getFlag(int x, int y) {
Pair<Integer, Integer> indices = getFlagIndex(x, y);
return RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()];
}
public void addFlag(int x, int y, int clipdata) {
int current = getFlag(x, y);
Pair<Integer, Integer> indices = getFlagIndex(x, y);
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = max(0, current) | clipdata;
}
public void removeFlag(int x, int y, int clipdata) {
int current = getFlag(x, y);
Pair<Integer, Integer> indices = getFlagIndex(x, y);
if ((current & clipdata) == 0) return;
current = max(0, current) & ~clipdata;
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = current;
}
public void clearFlag(int x, int y) {
Pair<Integer, Integer> indices = getFlagIndex(x, y);
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = 0;
}
public void invalidateFlag(int x, int y) {
Pair<Integer, Integer> indices = getFlagIndex(x, y);
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = -1;
}
/**
* Flags a door object (type 0-3).
* @param x The x-coordinate
@ -465,13 +496,6 @@ public final class RegionFlags {
}
}
/**
* Unloads the clipping flags.
*/
public void unload() {
clippingFlags = null;
}
/**
* Gets the members.
* @return The members.
@ -488,22 +512,6 @@ public final class RegionFlags {
this.members = members;
}
/**
* Gets the clippingFlags.
* @return The clippingFlags.
*/
public int[][] getClippingFlags() {
return clippingFlags;
}
/**
* Sets the clippingFlags.
* @param clippingFlags The clippingFlags to set.
*/
public void setClippingFlags(int[][] clippingFlags) {
this.clippingFlags = clippingFlags;
}
/**
* Gets the plane.
* @return The plane.

View file

@ -14,7 +14,6 @@ import java.util.List;
* @author Emperor
*/
public final class DumbPathfinder extends Pathfinder {
/**
* If a path can be found.
*/
@ -103,7 +102,7 @@ public final class DumbPathfinder extends Pathfinder {
found = true;
switch (dir) {
case NORTH:
if ((clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0) {
found = false;
break;
}
@ -111,7 +110,7 @@ public final class DumbPathfinder extends Pathfinder {
y++;
break;
case NORTH_EAST:
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 1) & 0x12c01e0) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 1) & PREVENT_NORTHEAST) != 0) {
found = false;
break;
}
@ -120,7 +119,7 @@ public final class DumbPathfinder extends Pathfinder {
y++;
break;
case EAST:
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0) {
found = false;
break;
}
@ -128,7 +127,7 @@ public final class DumbPathfinder extends Pathfinder {
x++;
break;
case SOUTH_EAST:
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & 0x12c0183) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & PREVENT_SOUTHEAST) != 0) {
found = false;
break;
}
@ -137,7 +136,7 @@ public final class DumbPathfinder extends Pathfinder {
y--;
break;
case SOUTH:
if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0) {
found = false;
break;
}
@ -145,7 +144,7 @@ public final class DumbPathfinder extends Pathfinder {
y--;
break;
case SOUTH_WEST:
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & 0x12c010e) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & PREVENT_SOUTHWEST) != 0) {
found = false;
break;
}
@ -154,7 +153,7 @@ public final class DumbPathfinder extends Pathfinder {
y--;
break;
case WEST:
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0) {
found = false;
break;
}
@ -162,7 +161,7 @@ public final class DumbPathfinder extends Pathfinder {
x--;
break;
case NORTH_WEST:
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & 0x12c0138) != 0) {
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & PREVENT_NORTHWEST) != 0) {
found = false;
break;
}

View file

@ -9,6 +9,15 @@ import core.game.world.map.RegionManager;
import rs09.game.world.map.path.SmartPathfinder;
public abstract class Pathfinder {
public static final int PREVENT_NORTH = 0x12c0120;
public static final int PREVENT_EAST = 0x12c0180;
public static final int PREVENT_NORTHEAST = 0x12c01e0;
public static final int PREVENT_SOUTH = 0x12c0102;
public static final int PREVENT_SOUTHEAST = 0x12c0183;
public static final int PREVENT_WEST = 0x12c0108;
public static final int PREVENT_SOUTHWEST = 0x12c010e;
public static final int PREVENT_NORTHWEST = 0x12c0138;
/**
* The smart path finder.

View file

@ -7,6 +7,8 @@ import core.game.node.item.Item;
import core.net.packet.IncomingPacket;
import core.net.packet.IoBuffer;
import static api.ContentAPIKt.getAttribute;
/**
* Represents the packet to handle an item slot switch.
* @author 'Vexia
@ -82,7 +84,7 @@ public class SlotSwitchPacket implements IncomingPacket {
final Item item = container.get(slot);
final Item second = container.get(secondSlot);
if (player.getInterfaceManager().hasChatbox()) {
if (player.getInterfaceManager().hasChatbox() && !getAttribute(player, "close_c_", false)) {
player.getInterfaceManager().closeChatbox();
switchItem(secondSlot,slot,container,insert,player);
container.refresh();

View file

@ -217,5 +217,8 @@ class ServerConstants {
@JvmField
var DISCORD_GE_WEBHOOK = ""
@JvmField
var PRELOAD_MAP = false
}
}

View file

@ -24,7 +24,9 @@ import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.context.Graphics
import core.game.world.update.flag.player.ChatFlag
import core.tools.RandomFunction
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -99,6 +101,11 @@ class ScriptAPI(private val bot: Player) {
return entity
}
fun sendChat(message: String) {
bot.sendChat(message)
bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0)))
}
/**
* Gets the nearest node with a name contained in the list of acceptable names
* @param acceptedNames the list of accepted npc/object names
@ -422,7 +429,10 @@ class ScriptAPI(private val bot: Player) {
* A function for teleporting the bot to the GE
* @author Ceikry
*/
fun teleportToGE(){
fun teleportToGE() : Boolean{
if (bot.isTeleBlocked) {
return false
}
bot.lock()
bot.visualize(ANIMATIONUP, GRAPHICSUP)
bot.impactHandler.disabledTicks = 4
@ -435,6 +445,7 @@ class ScriptAPI(private val bot: Player) {
return true
}
})
return true
}
/**
@ -548,7 +559,10 @@ class ScriptAPI(private val bot: Player) {
* @param loc the location to teleport to
* @author Ceikry
*/
fun teleport(loc: Location){
fun teleport(loc: Location) : Boolean {
if (bot.isTeleBlocked) {
return false
}
bot.lock()
bot.visualize(ANIMATIONUP, GRAPHICSUP)
bot.impactHandler.disabledTicks = 4
@ -561,6 +575,7 @@ class ScriptAPI(private val bot: Player) {
return true
}
})
return true
}
/**

View file

@ -457,8 +457,7 @@ class Adventurer(val style: CombatStyle): Script() {
.replace("@name", localPlayer.username)
.replace("@timer", until.toString())
bot.sendChat(chat)
bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0)))
scriptAPI.sendChat(chat)
}
enum class State{

View file

@ -8,6 +8,7 @@ import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.combat.InteractionType
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.entity.skill.prayer.BoneBuryingOptionPlugin
import core.game.node.entity.state.EntityState
import core.game.node.item.Item
import core.game.system.task.Pulse
@ -15,6 +16,7 @@ import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.impl.WildernessZone
import core.tools.RandomFunction
import org.rs09.consts.Items
import rs09.game.ai.AIRepository
import rs09.game.ai.pvmbots.CombatBotAssembler
@ -25,16 +27,24 @@ import rs09.game.node.entity.combat.handlers.RangeSwingHandler
import kotlin.random.Random
/**
* A bot script for killing green dragons in the wilderness.. Capable of banking, selling on ge, buying on ge, eating and more.
* A bot script for killing green dragons in the wilderness.. Capable of banking, selling on ge, eating, trash talking, buries bones when fleeing and more.
* @param style The combat style the bot is going to use.
* @param area (optional) What area the bot tries to kill dragons in.
* @author Ceikry
*/
class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Script() {
var state = State.KILLING
companion object {
val westDragons = ZoneBorders(2971,3606,2991,3628)
val wildernessLine = ZoneBorders(3078,3523,3096,3523)
val edgevilleLine = ZoneBorders(3078,3520,3096,3520)
val bankZone = ZoneBorders(3092,3489,3094,3493)
val trashTalkLines = arrayOf("Bro, seriously?", "Ffs.", "Jesus christ.", "????", "Friendly!", "Get a life dude", "Do you mind??? lol", "Lol.", "Kek.", "One sec burying all the bones.", "Yikes.", "Yeet", "Ah shit, here we go again.", "Cmonnnn", "Plz", "Do you have nothing better to do?", "Cmon bro pls", "I just need to get my prayer up bro jesus", "Reeeeeee", "I cant believe you've done this", "Really m8", "Zomg", "Aaaaaaaaaaaaaaaaaaaaa", "Rofl.", "Oh god oh fuck oh shit", "....", ":|", "A q p", "Hcim btw", "I hope the revenants kill your mum", "Wrap your ass titties", "Why do this", "Bruh", "Straight sussin no cap fr fr", "This ain't bussin dawg", "Really bro?")
}
var state = State.TO_BANK
var handler: CombatSwingHandler? = null
var lootDelay = 0
var offerMade = false
var trashTalkDelay = 0
var food = if (Random.nextBoolean()){
Items.LOBSTER_379
@ -45,28 +55,18 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
}
var myBorders: ZoneBorders? = null
val type = when(style){
CombatStyle.MELEE -> CombatBotAssembler.Type.MELEE
CombatStyle.MAGIC -> CombatBotAssembler.Type.MAGE
CombatStyle.RANGE -> CombatBotAssembler.Type.RANGE
}
val type = CombatBotAssembler.Type.MELEE
val westDragons = ZoneBorders(2971,3606,2991,3628)
val wildernessLine = ZoneBorders(3078,3523,3096,3523)
val edgevilleLine = ZoneBorders(3078,3520,3096,3520)
val bankZone = ZoneBorders(3092,3489,3094,3493)
override fun tick() {
if(!bot.isActive){
running = false
return
}
if(bot.inventory.getAmount(food) < 3 && state == State.KILLING)
state = State.TO_BANK
scriptAPI.eat(food)
checkFoodStockAndEat()
when(state){
State.KILLING -> {
bot.properties.combatPulse.temporaryHandler = handler
scriptAPI.attackNpcInRadius(bot,"Green dragon",20)
@ -86,11 +86,13 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
if(players.isEmpty()){
state = State.TO_DRAGONS
} else {
if(bot.skullManager.level < 21 && bot.stateManager.get(EntityState.TELEBLOCK) != null){
scriptAPI.teleportToGE()
state = State.REFRESHING
if(bot.skullManager.level < 21){
if (scriptAPI.teleportToGE())
state = State.REFRESHING
return
}
sendTrashTalk()
attemptToBuryBone()
scriptAPI.walkTo(WildernessZone.getInstance().borders.random().randomLoc)
}
}
@ -107,7 +109,7 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
}
return
}
items.forEach {it: Item -> scriptAPI.takeNearestGroundItem(it.id)}
items.toTypedArray().forEach {it: Item -> scriptAPI.takeNearestGroundItem(it.id)}
}
State.TO_BANK -> {
@ -157,25 +159,10 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
}
State.BUYING_FOOD -> {
if(!offerMade)
{
scriptAPI.buyFromGE(bot, food, 100)
offerMade = true
} else
{
val offer = AIRepository.getOffer(bot)
if (offer == null) {
offerMade = false
} else {
if (offer.completedAmount == offer.amount) {
state = State.TO_DRAGONS
offer.offerState = OfferState.REMOVED
bot.bank.add(Item(offer.itemID, offer.completedAmount))
bot.bank.refresh()
scriptAPI.withdraw(food, 10)
}
}
}
state = State.TO_DRAGONS
bot.bank.add(Item(food,50))
bot.bank.refresh()
scriptAPI.withdraw(food, 10)
}
State.TO_DRAGONS -> {
@ -236,13 +223,29 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
}
}
private fun attemptToBuryBone() {
if (bot.inventory.containsAtLeastOneItem(Items.DRAGON_BONES_536)) {
BoneBuryingOptionPlugin().handle(bot, bot.inventory.get(Item(Items.DRAGON_BONES_536)), "bury")
}
}
private fun checkFoodStockAndEat() {
if (bot.inventory.getAmount(food) < 3 && state == State.KILLING)
state = State.TO_BANK
scriptAPI.eat(food)
}
private fun sendTrashTalk() {
if (trashTalkDelay-- == 0)
scriptAPI.sendChat(trashTalkLines.random())
else
trashTalkDelay = RandomFunction.random(10, 30)
}
override fun newInstance(): Script {
val script = GreenDragonKiller(style)
val tier = CombatBotAssembler.Tier.HIGH
if(type == CombatBotAssembler.Type.RANGE)
script.bot = CombatBotAssembler().assembleRangeDragonBot(tier,bot.startLocation)
else
script.bot = CombatBotAssembler().assembleMeleeDragonBot(tier, bot.startLocation)
val tier = CombatBotAssembler.Tier.MED
script.bot = CombatBotAssembler().assembleMeleeDragonBot(tier, bot.startLocation)
return script
}
@ -262,11 +265,7 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
}
init {
handler = when(style){
CombatStyle.MELEE -> MeleeSwinger(this)
CombatStyle.MAGIC -> MageSwinger(this)
CombatStyle.RANGE -> RangeSwinger(this)
}
handler = MeleeSwinger(this)
equipment.add(Item(Items.ANTI_DRAGON_SHIELD_1540))
myBorders = westDragons
skills[Skills.AGILITY] = 99
@ -276,22 +275,10 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr
internal class MeleeSwinger(val script: GreenDragonKiller) : MeleeSwingHandler() {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
if(script.state == State.TO_BANK) {script.bot.pulseManager.current.stop()}
if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()}
return super.canSwing(entity, victim)
}
}
internal class MageSwinger(val script: GreenDragonKiller) : MagicSwingHandler() {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()}
return super.canSwing(entity, victim)
}
}
internal class RangeSwinger(val script: GreenDragonKiller) : RangeSwingHandler() {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()}
if(victim is Player || victim.name.contains("revenant", ignoreCase = true)) {
script.state = State.RUNNING
script.bot.pulseManager.current.stop()
}
return super.canSwing(entity, victim)
}
}

View file

@ -5,9 +5,11 @@ import core.game.content.dialogue.FacialExpression
import core.game.content.global.action.ClimbActionHandler
import core.game.content.global.action.DoorActionHandler
import core.game.node.scenery.Scenery
import core.game.system.task.Pulse
import core.game.world.map.Location
import org.rs09.consts.NPCs
import rs09.game.interaction.InteractionListener
import rs09.game.world.repository.Repository
/**
* Handles tutorial-specific node interactions
@ -19,6 +21,7 @@ class TutorialListeners : InteractionListener {
val COOKS_EXIT = 3018
val QUEST_ENTER = 3019
val QUEST_LADDER = 3029
val QUEST_EXIT_LADDER = 3028
val COMBAT_EXIT = 3030
val BANK_EXIT = 3024
val FINANCE_EXIT = 3025
@ -80,14 +83,30 @@ class TutorialListeners : InteractionListener {
}
on(QUEST_LADDER, SCENERY, "climb-down") {player, ladder ->
if(getAttribute(player, "tutorial:stage", 0) != 29)
if(getAttribute(player, "tutorial:stage", 0) < 29)
return@on true
setAttribute(player, "tutorial:stage", 30)
TutorialStage.load(player, 30)
if (getAttribute(player, "tutorial:stage", 0) == 29) {
setAttribute(player, "tutorial:stage", 30)
TutorialStage.load(player, 30)
}
ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-down")
}
on(QUEST_EXIT_LADDER, SCENERY, "climb-up") { player, ladder ->
ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-up")
submitWorldPulse(object : Pulse(2) {
override fun pulse(): Boolean {
val questTutor = Repository.findNPC(NPCs.QUEST_GUIDE_949) ?: return true
sendChat(questTutor, "What are you doing, ${player.username}? Get back down the ladder.")
return true
}
})
return@on true
}
on(COMBAT_GATES, SCENERY, "open"){player, gate ->
if(getAttribute(player, "tutorial:stage", 0) != 43)
return@on true

View file

@ -10,15 +10,19 @@ import core.game.node.entity.player.link.InterfaceManager
import core.game.node.entity.player.link.IronmanMode
import core.game.node.entity.player.link.TeleportManager
import core.game.node.item.Item
import core.game.system.communication.ClanRepository
import core.game.world.map.Location
import core.net.amsc.MSPacketRepository
import core.net.amsc.WorldCommunicator
import core.plugin.Initializable
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import proto.management.JoinClanRequest
import rs09.ServerConstants
import rs09.game.interaction.inter.RulesAndInfo
import rs09.game.world.GameWorld
import rs09.tools.END_DIALOGUE
import rs09.worker.ManagementEvents
/**
* Handles the magic tutor's dialogue
@ -179,6 +183,16 @@ class TutorialMagicTutorDialogue(player: Player? = null) : DialoguePlugin(player
player.unhook(TutorialInteractionReceiver)
player.unhook(TutorialButtonReceiver)
RulesAndInfo.openFor(player)
if (GameWorld.settings!!.enable_default_clan) {
player.communication.currentClan = ServerConstants.SERVER_NAME
val clanJoin = JoinClanRequest.newBuilder()
clanJoin.clanName = ServerConstants.SERVER_NAME
clanJoin.username = player.name
ManagementEvents.publish(clanJoin.build())
}
}
12 -> {

View file

@ -1,7 +1,6 @@
package rs09.game.node.entity.combat
import core.game.component.Component
import core.game.container.Container
import core.game.container.impl.EquipmentContainer
import core.game.node.Node
import core.game.node.entity.Entity
@ -15,8 +14,12 @@ import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.player.link.prayer.PrayerType
import core.game.node.entity.skill.Skills
import core.game.node.entity.skill.summoning.familiar.Familiar
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.RegionManager.getClippingFlag
import core.game.world.map.path.Pathfinder
import core.game.world.map.path.Pathfinder.*
import core.game.world.update.flag.context.Animation
import core.tools.RandomFunction
import rs09.game.system.SystemLogger
@ -207,6 +210,11 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
* @return `True` if so.
*/
open fun isAttackable(entity: Entity, victim: Entity): InteractionType? {
if (type == CombatStyle.MELEE) {
val stepType = canStepTowards(entity, victim)
if (stepType != InteractionType.STILL_INTERACT) return stepType
}
val comp = entity.getAttribute("autocast_component",null) as Component?
if((comp != null || type == CombatStyle.MAGIC) && (entity.properties.autocastSpell == null || entity.properties.autocastSpell.spellId == 0) && entity is Player){
val weapEx = entity.getExtension<Any>(WeaponInterface::class.java) as WeaponInterface?
@ -235,6 +243,74 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
return InteractionType.STILL_INTERACT
}
private fun canStepTowards(entity: Entity, victim: Entity): InteractionType {
val closestVictimTile = victim.getClosestOccupiedTile(entity.location)
val closestEntityTile = entity.getClosestOccupiedTile(closestVictimTile)
val dir = closestEntityTile.deriveDirection(closestVictimTile)
?: return InteractionType.STILL_INTERACT //if we cannot derive a direction, it's because both tiles are the same, so hand off control to the main logic which already handles this case
var next = closestEntityTile
while (next.getDistance(closestVictimTile) > 3) { //skip the initial gap in distance if it exists, because standard pathfinding would already stop us before this point if something was between us and the NPC or vice versa
next = next.transform(dir)
}
var result: InteractionType = InteractionType.STILL_INTERACT
val maxIterations = next.getDistance(closestVictimTile).toInt()
for (i in 0..maxIterations) { //step towards the target tile, checking if anything would obstruct us on the way, and immediately breaking + returning if it does.
next = next.transform(dir)
result = checkStepInterval(dir, next)
if (result == InteractionType.NO_INTERACT) break
}
return result
}
private fun checkStepInterval(
dir: Direction,
next: Location
): InteractionType {
val components = next.getStepComponents(dir)
when (dir) {
Direction.NORTH -> if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT
Direction.EAST -> if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT
Direction.SOUTH -> if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT
Direction.WEST -> if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT
Direction.NORTH_EAST -> {
if (getClippingFlag(components[0]) and PREVENT_EAST != 0
|| getClippingFlag(components[1]) and PREVENT_NORTH != 0
|| getClippingFlag(next) and PREVENT_NORTHEAST != 0
) return InteractionType.NO_INTERACT
}
Direction.NORTH_WEST -> {
if (getClippingFlag(components[0]) and PREVENT_WEST != 0
|| getClippingFlag(components[1]) and PREVENT_NORTH != 0
|| getClippingFlag(next) and PREVENT_NORTHWEST != 0
) return InteractionType.NO_INTERACT
}
Direction.SOUTH_EAST -> {
if (getClippingFlag(components[0]) and PREVENT_EAST != 0
|| getClippingFlag(components[1]) and PREVENT_SOUTH != 0
|| getClippingFlag(next) and PREVENT_SOUTHEAST != 0
) return InteractionType.NO_INTERACT
}
Direction.SOUTH_WEST -> {
if (getClippingFlag(components[0]) and PREVENT_WEST != 0
|| getClippingFlag(components[1]) and PREVENT_SOUTH != 0
|| getClippingFlag(next) and PREVENT_SOUTHWEST != 0
) return InteractionType.NO_INTERACT
}
}
return InteractionType.STILL_INTERACT
}
/**
* Gets the dragonfire message.
* @param protection The protection value.

View file

@ -55,6 +55,9 @@ class ModernListeners : SpellListener("modern"){
override fun defineListeners() {
onCast(Modern.HOME_TELEPORT,NONE){player, _ ->
if (!getAttribute(player, "tutorial:complete", false)) {
return@onCast
}
requires(player)
player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME)
setDelay(player,true)

View file

@ -115,6 +115,7 @@ object ServerConfigParser {
ServerConstants.SERVER_GE_NAME = data.getString("world.name_ge") ?: ServerConstants.SERVER_NAME
ServerConstants.RULES_AND_INFO_ENABLED = data.getBoolean("world.show_rules", true)
ServerConstants.BOTS_INFLUENCE_PRICE_INDEX = data.getBoolean("world.bots_influence_ge_price", true)
ServerConstants.PRELOAD_MAP = data.getBoolean("server.preload_map", false)
ServerConstants.REVENANT_POPULATION = data.getLong("world.revenant_population", 30L).toInt()
ServerConstants.BANK_BOOTH_QUICK_OPEN = data.getBoolean("world.bank_booth_quick_open", false)
ServerConstants.DISCORD_GE_WEBHOOK = data.getString("server.discord_webhook", "")

View file

@ -9,6 +9,7 @@ import core.game.system.SystemState
import core.game.system.task.Pulse
import core.game.system.task.TaskExecutor
import core.game.world.map.Location
import core.game.world.map.Region
import core.game.world.map.RegionManager
import core.plugin.CorePluginTypes.StartupPlugin
import core.tools.RandomFunction
@ -179,6 +180,12 @@ object GameWorld {
SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE)
}
SceneryDefinition.getDefinitions().values.forEach(Consumer { obj: SceneryDefinition -> obj.examine })
if (ServerConstants.PRELOAD_MAP) {
//force early loading of all commonly accessed regions to improve performance at the cost of memory usage
(7483..15420).forEach { id -> RegionManager.forId(id).also { Region.load(it) } }
}
System.gc()
SystemLogger.initTradeLogger()
}

View file

@ -31,7 +31,7 @@ class ImmerseWorld : StartupListener {
immerseSeersAndCatherby()
immerseLumbridgeDraynor()
immerseVarrock()
// immerseWilderness() temp disabled due to unbalanced exchange rates
immerseWilderness()
immerseFishingGuild()
immerseAdventurer()
// immerseSlayer()
@ -193,20 +193,13 @@ class ImmerseWorld : StartupListener {
}
fun immerseWilderness() {
val wilderness = Location.create(2979, 3603, 0)
for (i in (0..1)) {
GeneralBotCreator(
GreenDragonKiller(CombatStyle.MELEE),
assembler.assembleMeleeDragonBot(CombatBotAssembler.Tier.HIGH, wilderness)
)
GeneralBotCreator(
val wilderness = Location.create(3092, 3493, 0)
repeat(6) {
GeneralBotCreator (
GreenDragonKiller(CombatStyle.MELEE),
assembler.assembleMeleeDragonBot(CombatBotAssembler.Tier.MED, wilderness)
)
GeneralBotCreator(
GreenDragonKiller(CombatStyle.RANGE),
assembler.assembleRangedBot(CombatBotAssembler.Tier.HIGH, wilderness)
)
}
}

View file

@ -106,7 +106,7 @@ class MajorUpdateWorker {
//remove all null or finished pulses from the list
rmlist.forEach {
if (GameWorld.Pulser.TASKS.contains(it)) GameWorld.Pulser.TASKS.remove(it)
GameWorld.Pulser.TASKS.remove(it)
}
rmlist.clear()

View file

@ -30,6 +30,8 @@ import proto.management.SendClanInfo
import proto.management.SendClanInfo.ClanMember
import proto.management.SendContactInfo
import proto.management.SendContactInfo.Contact
import rs09.ServerConstants
import rs09.auth.UserAccountInfo
import rs09.game.system.SystemLogger
import rs09.game.world.GameWorld
import rs09.game.world.repository.Repository
@ -262,40 +264,29 @@ object ManagementEvents {
is SendClanInfo -> {
if (event.hasInfo) {
val clan = ClanRepository.getClans().getOrPut(event.clanOwner) { ClanRepository(event.clanOwner) }
clan.name = event.clanName
clan.joinRequirement = ClanRank.values()[event.joinRequirement]
clan.kickRequirement = ClanRank.values()[event.kickRequirement]
clan.messageRequirement = ClanRank.values()[event.messageRequirement]
clan.lootRequirement = ClanRank.values()[event.lootRequirement]
for (member in event.membersList) {
val entry = ClanEntry(member.username, member.world)
clan.ranks[member.username] = ClanRank.values()[member.rank]
if (member.world == GameWorld.settings!!.worldId) {
val p = Repository.getPlayerByName(member.username)
entry.player = p
p?.communication?.clan = clan
}
clan.players.add(entry)
}
clan.update()
initializeClanFrom(event)
} else {
val info = GameWorld.accountStorage.getAccountInfo(event.clanOwner)
var info = GameWorld.accountStorage.getAccountInfo(event.clanOwner)
if (info.clanName.isNotEmpty()) {
val reqs = CommunicationInfo.parseClanRequirements(info.clanReqs)
val c = ClanRepository(event.clanOwner)
val contacts = CommunicationInfo.parseContacts(info.contacts)
c.name = info.clanName
c.joinRequirement = reqs[0]
c.messageRequirement = reqs[1]
c.kickRequirement = reqs[2]
c.lootRequirement = reqs[3]
for ((username, contact) in contacts) {
c.ranks[username] = contact.rank
initializeClanWith(info)
} else {
SystemLogger.logMS("Creating default server clan")
if (GameWorld.settings!!.enable_default_clan && event.clanOwner == ServerConstants.SERVER_NAME) {
//Create a user with the default clan and some basic settings and stick them in the account storage
if (info == UserAccountInfo.createDefault()) {
info.username = ServerConstants.SERVER_NAME
info.password = ServerConstants.MS_SECRET_KEY
info.rights = 2
SystemLogger.logAlert("Creating default server account: ${info.username}, password is your MS_SECRET_KEY!")
GameWorld.authenticator.createAccountWith(info)
info = GameWorld.accountStorage.getAccountInfo(event.clanOwner)
}
info.clanName = "Global"
info.clanReqs = "-1,-1,7,7" //Any join, any message, owner kick, owner loot
GameWorld.accountStorage.update(info)
initializeClanWith(info)
}
ClanRepository.getClans()[event.clanOwner] = c
}
}
@ -323,7 +314,44 @@ object ManagementEvents {
}
}
private fun initializeClanFrom(event: SendClanInfo) {
val clan = ClanRepository.getClans().getOrPut(event.clanOwner) { ClanRepository(event.clanOwner) }
clan.name = event.clanName
clan.joinRequirement = ClanRank.values()[event.joinRequirement]
clan.kickRequirement = ClanRank.values()[event.kickRequirement]
clan.messageRequirement = ClanRank.values()[event.messageRequirement]
clan.lootRequirement = ClanRank.values()[event.lootRequirement]
for (member in event.membersList) {
val entry = ClanEntry(member.username, member.world)
clan.ranks[member.username] = ClanRank.values()[member.rank]
if (member.world == GameWorld.settings!!.worldId) {
val p = Repository.getPlayerByName(member.username)
entry.player = p
p?.communication?.clan = clan
}
clan.players.add(entry)
}
clan.update()
}
private fun initializeClanWith(info: UserAccountInfo) {
val reqs = CommunicationInfo.parseClanRequirements(info.clanReqs)
val c = ClanRepository(info.username)
val contacts = CommunicationInfo.parseContacts(info.contacts)
c.name = info.clanName
c.joinRequirement = reqs[0]
c.messageRequirement = reqs[1]
c.kickRequirement = reqs[2]
c.lootRequirement = reqs[3]
for ((username, contact) in contacts) {
c.ranks[username] = contact.rank
}
ClanRepository.getClans()[info.username] = c
}
private fun queueUntilClanInfo(clanName: String, message: Message) {
val queue = waitingOnClanInfo.getOrPut(clanName) {LinkedList()}
queue.offer(message)

View file

@ -4,6 +4,9 @@
secret_key = "2009scape_development"
write_logs = true
msip = "127.0.0.1"
#preload the map (Increases memory usage by 2GB but makes game ticks smoother)
preload_map = false
[database]
database_name = "global"
@ -15,6 +18,8 @@ database_port = "3306"
[world]
name = "2009scape"
#name used for announcements of bots selling items on the GE
name_ge = "2009scape"
debug = true
dev = true
start_gui = false
@ -49,6 +54,12 @@ wild_pvp_enabled = false
jad_practice_enabled = true
personalized_shops = true
bots_influence_ge_price = true
#verbose cutscene logging (for cutscenes in the new system)
verbose_cutscene = false
#show the rules the first time a player logs in
show_rules = true
#the number of revenants active at a time
revenant_population = 30
[paths]
#path to the data folder, which contains the cache subfolder and such