Fix sector code (hopefully for the last time) and resolve order conflicts from the use of hashCode etc; add hashCode and equals to any class extending Entity; remove magic constant when resetting the interacting mob.

This commit is contained in:
Major-
2015-01-12 02:45:35 +00:00
parent 2c7ecdad80
commit 91f9c49d0a
23 changed files with 340 additions and 187 deletions
@@ -1,6 +1,7 @@
package org.apollo.game.message.handler.impl;
import java.util.List;
import java.util.Set;
import org.apollo.game.message.handler.MessageHandler;
import org.apollo.game.message.handler.MessageHandlerContext;
@@ -36,14 +37,9 @@ public final class ObjectActionVerificationHandler extends MessageHandler<Object
Position position = message.getPosition();
Sector sector = repository.fromPosition(position);
List<GameObject> objects = sector.getEntities(position, EntityType.GAME_OBJECT);
Set<GameObject> objects = sector.getEntities(position, EntityType.GAME_OBJECT);
if (!containsObject(id, objects)) {
ctx.breakHandlerChain();
return;
}
if (!player.getPosition().isWithinDistance(position, 15)) {
if (!player.getPosition().isWithinDistance(position, 15) || !containsObject(id, objects)) {
ctx.breakHandlerChain();
return;
}
@@ -62,8 +58,8 @@ public final class ObjectActionVerificationHandler extends MessageHandler<Object
* @param objects The list of objects.
* @return {@code true} if the list does contain the object with the specified id, otherwise {@code false}.
*/
private static boolean containsObject(int id, List<GameObject> objects) {
return objects.stream().filter(object -> object.getId() == id).findAny().isPresent();
private static boolean containsObject(int id, Set<GameObject> objects) {
return objects.stream().anyMatch(object -> object.getId() == id);
}
}
+31 -8
View File
@@ -1,5 +1,8 @@
package org.apollo.game.model;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.area.SectorCoordinates;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
@@ -23,7 +26,7 @@ public final class Position {
/**
* The packed integer containing the {@code height, x}, and {@code y} variables.
*/
private final int position;
private final int packed;
/**
* Creates a position at the default height.
@@ -45,14 +48,14 @@ public final class Position {
public Position(int x, int y, int height) {
Preconditions.checkArgument(height >= 0 && height < HEIGHT_LEVELS, "Height level out of bounds.");
position = height << 30 | (y & 0x7FFF) << 15 | x & 0x7FFF;
packed = height << 30 | (y & 0x7FFF) << 15 | x & 0x7FFF;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Position) {
Position other = (Position) obj;
return position == other.position;
return packed == other.packed;
}
return false;
@@ -94,7 +97,7 @@ public final class Position {
* @return The height level.
*/
public int getHeight() {
return position >> 30;
return packed >> 30;
}
/**
@@ -147,6 +150,15 @@ public final class Position {
return Math.max(deltaX, deltaY);
}
/**
* Returns the {@link SectorCoordinates} of the {@link Sector} this position is inside.
*
* @return The sector coordinates.
*/
public SectorCoordinates getSectorCoordinates() {
return SectorCoordinates.fromPosition(this);
}
/**
* Gets the x coordinate of the sector this position is in.
*
@@ -171,7 +183,7 @@ public final class Position {
* @return The x coordinate.
*/
public int getX() {
return position & 0x7FFF;
return packed & 0x7FFF;
}
/**
@@ -180,12 +192,23 @@ public final class Position {
* @return The y coordinate.
*/
public int getY() {
return (position >> 15) & 0x7FFF;
return (packed >> 15) & 0x7FFF;
}
@Override
public int hashCode() {
return position;
return packed;
}
/**
* Returns whether or not this position is inside the specified {@link Sector}.
*
* @param sector The sector.
* @return {@code true} if this position is inside the specified sector, otherwise {@code false}.
*/
public boolean inside(Sector sector) {
SectorCoordinates coordinates = sector.getCoordinates();
return coordinates.equals(getSectorCoordinates());
}
/**
@@ -204,7 +227,7 @@ public final class Position {
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("x", getX()).add("y", getY()).add("height", getHeight())
.add("sector x", getTopLeftSectorX()).add("sector y", getTopLeftSectorY()).toString();
.add("sector", getSectorCoordinates()).toString();
}
}
+20 -26
View File
@@ -3,6 +3,7 @@ package org.apollo.game.model;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
@@ -133,7 +134,7 @@ public final class World {
/**
* This world's {@link SectorRepository}.
*/
private final SectorRepository sectorRepository = new SectorRepository(false);
private final SectorRepository sectors = SectorRepository.immutable();
/**
* Creates the world.
@@ -222,7 +223,7 @@ public final class World {
* @return The sector repository.
*/
public SectorRepository getSectorRepository() {
return sectorRepository;
return sectors;
}
/**
@@ -236,10 +237,10 @@ public final class World {
public void init(int release, IndexedFileSystem fs, PluginManager manager) throws Exception {
this.releaseNumber = release;
ItemDefinitionDecoder itemDefDecoder = new ItemDefinitionDecoder(fs);
ItemDefinition[] itemDefs = itemDefDecoder.decode();
ItemDefinition.init(itemDefs);
logger.fine("Loaded " + itemDefs.length + " item definitions.");
ItemDefinitionDecoder itemDecoder = new ItemDefinitionDecoder(fs);
ItemDefinition[] items = itemDecoder.decode();
ItemDefinition.init(items);
logger.fine("Loaded " + items.length + " item definitions.");
try (InputStream is = new BufferedInputStream(new FileInputStream("data/equipment-" + release + ".dat"))) {
EquipmentDefinitionParser parser = new EquipmentDefinitionParser(is);
@@ -249,14 +250,14 @@ public final class World {
}
NpcDefinitionDecoder npcDecoder = new NpcDefinitionDecoder(fs);
NpcDefinition[] npcDefs = npcDecoder.decode();
NpcDefinition.init(npcDefs);
logger.fine("Loaded " + npcDefs.length + " npc definitions.");
NpcDefinition[] npcs = npcDecoder.decode();
NpcDefinition.init(npcs);
logger.fine("Loaded " + npcs.length + " npc definitions.");
ObjectDefinitionDecoder objectDecoder = new ObjectDefinitionDecoder(fs);
ObjectDefinition[] objDefs = objectDecoder.decode();
ObjectDefinition.init(objDefs);
logger.fine("Loaded " + objDefs.length + " object definitions.");
ObjectDefinition[] objectDefs = objectDecoder.decode();
ObjectDefinition.init(objectDefs);
logger.fine("Loaded " + objectDefs.length + " object definitions.");
GameObjectDecoder staticDecoder = new GameObjectDecoder(fs);
GameObject[] objects = staticDecoder.decode();
@@ -283,10 +284,7 @@ public final class World {
* @param entities The entities.
*/
private void placeEntities(Entity... entities) {
for (Entity entity : entities) {
Sector sector = sectorRepository.fromPosition(entity.getPosition());
sector.addEntity(entity);
}
Arrays.stream(entities).forEach(entity -> sectors.fromPosition(entity.getPosition()).addEntity(entity));
}
/**
@@ -306,7 +304,7 @@ public final class World {
boolean success = npcRepository.add(npc);
if (success) {
Sector sector = sectorRepository.fromPosition(npc.getPosition());
Sector sector = sectors.fromPosition(npc.getPosition());
sector.addEntity(npc);
} else {
logger.warning("Failed to register npc, repository capacity reached: [count=" + npcRepository.size() + "]");
@@ -329,7 +327,7 @@ public final class World {
boolean success = playerRepository.add(player);
if (success) {
players.put(NameUtil.encodeBase37(username), player);
Sector sector = sectorRepository.fromPosition(player.getPosition());
Sector sector = sectors.fromPosition(player.getPosition());
sector.addEntity(player);
logger.info("Registered player: " + player + " [count=" + playerRepository.size() + "]");
@@ -357,11 +355,9 @@ public final class World {
*/
public void unregister(final Npc npc) {
if (npcRepository.remove(npc)) {
Sector sector = sectorRepository.fromPosition(npc.getPosition());
Sector sector = sectors.fromPosition(npc.getPosition());
if (!sector.removeEntity(npc)) {
logger.warning("Could not remove npc from their sector.");
}
sector.removeEntity(npc);
} else {
logger.warning("Could not find npc " + npc + " to unregister!");
}
@@ -377,10 +373,8 @@ public final class World {
players.remove(NameUtil.encodeBase37(player.getUsername()));
logger.info("Unregistered player: " + player + " [count=" + playerRepository.size() + "]");
Sector sector = sectorRepository.fromPosition(player.getPosition());
if (!sector.removeEntity(player)) {
logger.warning("Could not remove player from their sector.");
}
Sector sector = sectors.fromPosition(player.getPosition());
sector.removeEntity(player);
logoutDispatcher.dispatch(player);
} else {
+47 -42
View File
@@ -2,15 +2,19 @@ package org.apollo.game.model.area;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apollo.game.model.Position;
import org.apollo.game.model.entity.Entity;
import org.apollo.game.model.entity.Entity.EntityType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* An 8x8 area of the map.
@@ -19,6 +23,11 @@ import com.google.common.collect.ImmutableList;
*/
public final class Sector {
/**
* The default size of newly-created sets, to reduce memory usage.
*/
private static final int DEFAULT_SET_SIZE = 2;
/**
* The width and length of a sector, in tiles.
*/
@@ -32,7 +41,7 @@ public final class Sector {
/**
* A map of positions to entities in that position.
*/
private final Map<Position, List<Entity>> entities = new HashMap<>();
private final Map<Position, Set<Entity>> entities = new HashMap<>();
/**
* A list of listeners registered to this sector.
@@ -66,27 +75,37 @@ public final class Sector {
*/
public void addEntity(Entity entity) {
Position position = entity.getPosition();
List<Entity> entities = this.entities.get(position);
if (entities == null) {
entities = new ArrayList<>();
}
checkPosition(position);
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
entities.add(entity);
this.entities.put(position, entities);
local.add(entity);
notifyListeners(entity, SectorOperation.ADD);
}
/**
* Checks that the specified {@link Position} is included in this sector.
*
* @param position The position.
* @throws IllegalArgumentException If the specified position is not included in this sector.
*/
private void checkPosition(Position position) {
Preconditions.checkArgument(coordinates.equals(SectorCoordinates.fromPosition(position)),
"Position is not included in this sector.");
}
/**
* Checks if this sector contains the specified entity.
* <p>
* This method operates in constant time.
*
* @param entity The entity.
* @return {@code true} if this sector contains the entity, otherwise {@code false}.
*/
public boolean contains(Entity entity) {
Position position = entity.getPosition();
List<Entity> entities = this.entities.get(position);
Set<Entity> local = entities.get(position);
return entities != null && entities.contains(entity);
return local != null && local.contains(entity);
}
/**
@@ -98,17 +117,6 @@ public final class Sector {
return coordinates;
}
/**
* Gets an {@link ImmutableList} containing every {@link Entity} in this sector.
*
* @return The list.
*/
public List<Entity> getEntities() {
List<Entity> combined = new ArrayList<>();
this.entities.values().forEach(entities -> combined.addAll(entities));
return ImmutableList.copyOf(combined);
}
/**
* Gets a shallow copy of the {@link List} of {@link Entity} objects at the specified {@link Position}. The returned
* type will be {@link ImmutableList}.
@@ -117,13 +125,7 @@ public final class Sector {
* @return The list.
*/
public List<Entity> getEntities(Position position) {
List<Entity> entities = this.entities.get(position);
if (entities == null) {
this.entities.put(position, new ArrayList<>());
return ImmutableList.of();
}
return ImmutableList.copyOf(entities);
return ImmutableList.copyOf(entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE)));
}
/**
@@ -135,16 +137,12 @@ public final class Sector {
* @param type The {@link EntityType}.
* @return The list of entities.
*/
public <T extends Entity> List<T> getEntities(Position position, EntityType type) {
List<Entity> entities = this.entities.get(position);
if (entities == null) {
this.entities.put(position, new ArrayList<>());
return ImmutableList.of();
}
public <T extends Entity> Set<T> getEntities(Position position, EntityType type) {
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
@SuppressWarnings("unchecked")
List<T> filtered = (List<T>) entities.stream().filter(e -> e.getEntityType() == type).collect(Collectors.toList());
return ImmutableList.copyOf(filtered);
Set<T> filtered = (Set<T>) local.stream().filter(entity -> entity.getEntityType() == type).collect(Collectors.toSet());
return ImmutableSet.copyOf(filtered);
}
/**
@@ -161,17 +159,24 @@ public final class Sector {
* Removes a {@link Entity} from this sector.
*
* @param entity The entity.
* @return {@code true} if the entity was removed, otherwise {@code false}.
* @throws IllegalArgumentException If the entity does not belong in this sector, or if it was never added.
*/
public boolean removeEntity(Entity entity) {
Position position = entity.getPosition();
List<Entity> entities = this.entities.get(position);
public void removeEntity(Entity entity) {
try {
Position position = entity.getPosition();
checkPosition(position);
Set<Entity> local = entities.get(position);
if (local == null || !local.remove(entity)) {
throw new IllegalArgumentException("Entity belongs in this sector but does not exist.");
}
if (entities != null && entities.remove(entity)) {
notifyListeners(entity, SectorOperation.REMOVE);
return true;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return false;
}
}
@@ -2,11 +2,14 @@ package org.apollo.game.model.area;
import org.apollo.game.model.Position;
import com.google.common.base.MoreObjects;
/**
* An immutable class representing the coordinates of a sector, where the coordinates ({@code x, y}) are the top-left of
* the sector.
*
* @author Graham
* @author Major
*/
public final class SectorCoordinates {
@@ -43,12 +46,12 @@ public final class SectorCoordinates {
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
if (obj instanceof SectorCoordinates) {
SectorCoordinates other = (SectorCoordinates) obj;
return x == other.x && y == other.y;
}
final SectorCoordinates other = (SectorCoordinates) obj;
return x == other.x && y == other.y;
return false;
}
/**
@@ -74,4 +77,9 @@ public final class SectorCoordinates {
return x << 16 | y;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("x", x).add("y", y).toString();
}
}
@@ -17,6 +17,27 @@ import com.google.common.collect.ImmutableList;
*/
public final class SectorRepository {
/**
* Returns an immutable sector repository, where {@link Sector}s cannot be added or removed.
* <p>
* Note that, internally, sectors are added lazily (i.e. only when necessary). As such, repositories are (again,
* internally) not actually immutable, so do not rely on such behaviour.
*
* @return The sector repository.
*/
public static SectorRepository immutable() {
return new SectorRepository(false);
}
/**
* Returns a mutable sector repository, where {@link Sector}s may be removed.
*
* @return The sector repository.
*/
public static SectorRepository mutable() {
return new SectorRepository(true);
}
/**
* Whether or not sectors can be removed from this repository.
*/
@@ -32,7 +53,7 @@ public final class SectorRepository {
*
* @param permitRemoval If removal (of {@link Sector}s) from this repository should be permitted.
*/
public SectorRepository(boolean permitRemoval) {
private SectorRepository(boolean permitRemoval) {
this.permitRemoval = permitRemoval;
}
@@ -44,11 +65,12 @@ public final class SectorRepository {
* @throws UnsupportedOperationException If the coordinates of the provided sector are already mapped (and hence the
* existing sector would be replaced), and removal of sectors is not permitted.
*/
public void add(Sector sector) {
private void add(Sector sector) {
Preconditions.checkNotNull(sector, "Sector cannot be null.");
if (sectors.containsKey(sector.getCoordinates()) && !permitRemoval) {
throw new UnsupportedOperationException("Cannot add a sector with the same coordinates as an existing sector.");
}
sectors.put(sector.getCoordinates(), sector);
}
@@ -97,6 +119,7 @@ public final class SectorRepository {
sector = new Sector(coordinates);
add(sector);
}
return sector;
}
@@ -121,6 +144,7 @@ public final class SectorRepository {
if (!permitRemoval) {
throw new UnsupportedOperationException("Cannot remove sectors from this repository.");
}
return sectors.remove(sector.getCoordinates()) != null;
}
@@ -73,4 +73,10 @@ public abstract class Entity {
return position;
}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
}
@@ -31,6 +31,16 @@ public final class GameObject extends Entity {
this.packed = id << 8 | type << 2 | orientation;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GameObject) {
GameObject other = (GameObject) obj;
return position.equals(other.position) && packed == other.packed;
}
return false;
}
/**
* Gets the definition of this object.
*
@@ -72,6 +82,11 @@ public final class GameObject extends Entity {
return (packed >> 2) & 0x3F;
}
@Override
public int hashCode() {
return packed;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("id", getId()).add("type", getType()).add("rotation", getRotation())
+68 -46
View File
@@ -3,6 +3,7 @@ package org.apollo.game.model.entity;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apollo.game.action.Action;
import org.apollo.game.model.Animation;
@@ -11,7 +12,6 @@ import org.apollo.game.model.Graphic;
import org.apollo.game.model.Position;
import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.area.SectorCoordinates;
import org.apollo.game.model.area.SectorRepository;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.game.model.entity.attr.Attribute;
@@ -20,6 +20,7 @@ import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.inv.Inventory.StackMode;
import org.apollo.game.model.inv.InventoryConstants;
import org.apollo.game.scheduling.impl.SkillNormalizationTask;
import org.apollo.game.sync.block.InteractingMobBlock;
import org.apollo.game.sync.block.SynchronizationBlock;
import org.apollo.game.sync.block.SynchronizationBlockSet;
@@ -31,11 +32,6 @@ import org.apollo.game.sync.block.SynchronizationBlockSet;
*/
public abstract class Mob extends Entity {
/**
* This mob's current action.
*/
private transient Action<?> action;
/**
* The attribute map of this mob.
*/
@@ -49,23 +45,13 @@ public abstract class Mob extends Entity {
/**
* This mob's npc definition. A player only uses this if they are appearing as an npc.
*/
protected NpcDefinition definition;
protected Optional<NpcDefinition> definition;
/**
* This mob's equipment.
*/
protected final Inventory equipment = new Inventory(InventoryConstants.EQUIPMENT_CAPACITY, StackMode.STACK_ALWAYS);
/**
* The position this mob is facing towards.
*/
private transient Position facingPosition = position;
/**
* This mob's first movement direction.
*/
private transient Direction firstDirection = Direction.NONE;
/**
* The index of this mob.
*/
@@ -81,6 +67,31 @@ public abstract class Mob extends Entity {
*/
protected final Inventory inventory = new Inventory(InventoryConstants.INVENTORY_CAPACITY);
/**
* This mob's skill set.
*/
protected final SkillSet skillSet = new SkillSet();
/**
* This mob's walking queue.
*/
protected final transient WalkingQueue walkingQueue = new WalkingQueue(this);
/**
* This mob's current action.
*/
private transient Action<?> action;
/**
* The position this mob is facing towards.
*/
private transient Position facingPosition = position;
/**
* This mob's first movement direction.
*/
private transient Direction firstDirection = Direction.NONE;
/**
* This mob's list of local npcs.
*/
@@ -96,28 +107,30 @@ public abstract class Mob extends Entity {
*/
private transient Direction secondDirection = Direction.NONE;
/**
* This mob's skill set.
*/
protected final SkillSet skillSet = new SkillSet();
/**
* Indicates whether this mob is currently teleporting or not.
*/
private transient boolean teleporting = false;
/**
* This mob's walking queue.
*/
protected final transient WalkingQueue walkingQueue = new WalkingQueue(this);
/**
* Creates a new mob with the specified initial {@link Position}.
*
* @param position The initial position.
* Creates the mob with the specified initial {@link Position}.
*
* @param position The position.
*/
public Mob(Position position) {
this(position, null);
}
/**
* Creates the mob.
*
* @param position The initial position.
* @param definition The {@link NpcDefinition}.
*/
public Mob(Position position, NpcDefinition definition) {
super(position);
this.definition = Optional.ofNullable(definition);
init();
}
@@ -170,7 +183,7 @@ public abstract class Mob extends Entity {
* @return The npc definition.
*/
public final NpcDefinition getDefinition() {
return definition;
return definition.get();
}
/**
@@ -183,6 +196,7 @@ public abstract class Mob extends Entity {
return secondDirection == Direction.NONE ? new Direction[] { firstDirection } : new Direction[] { firstDirection,
secondDirection };
}
return Direction.EMPTY_DIRECTION_ARRAY;
}
@@ -288,10 +302,12 @@ public abstract class Mob extends Entity {
}
/**
* Initialises this mob.
* Returns whether or not this mob has an {@link NpcDefinition}.
*
* @return {@code true} if this mob has an npc definition, {@code false} if not.
*/
private void init() {
World.getWorld().schedule(new SkillNormalizationTask(this));
public final boolean hasNpcDefinition() {
return definition.isPresent();
}
/**
@@ -342,7 +358,7 @@ public abstract class Mob extends Entity {
*/
public final void resetInteractingMob() {
interactingMob = null;
blockSet.add(SynchronizationBlock.createInteractingMobBlock(65535));
blockSet.add(SynchronizationBlock.createInteractingMobBlock(InteractingMobBlock.RESET_INDEX));
}
/**
@@ -358,10 +374,11 @@ public abstract class Mob extends Entity {
/**
* Sets this mob's {@link NpcDefinition}.
*
* @param definition The definition.
* @param definition The definition. Must not be {@code null}.
* @throws NullPointerException If the specified definition is {@code null}.
*/
public final void setDefinition(NpcDefinition definition) {
this.definition = definition;
this.definition = Optional.of(definition);
}
/**
@@ -403,17 +420,14 @@ public abstract class Mob extends Entity {
*/
public final void setPosition(Position position) {
SectorRepository repository = World.getWorld().getSectorRepository();
Sector newSector = repository.fromPosition(position);
Sector current = repository.fromPosition(this.position);
if (SectorCoordinates.fromPosition(this.position) != SectorCoordinates.fromPosition(position)) {
Sector oldSector = repository.fromPosition(this.position);
oldSector.removeEntity(this);
} else {
newSector.removeEntity(this);
}
Sector next = position.inside(current) ? current : repository.fromPosition(position);
this.position = position;
newSector.addEntity(this);
current.removeEntity(this);
this.position = position; // addEntity relies on the position being updated, so do that first.
next.addEntity(this);
}
/**
@@ -446,6 +460,7 @@ public abstract class Mob extends Entity {
if (this.action.equals(action)) {
return false;
}
stopAction();
}
@@ -500,4 +515,11 @@ public abstract class Mob extends Entity {
blockSet.add(SynchronizationBlock.createTurnToPositionBlock(position));
}
/**
* Initialises this mob.
*/
private void init() {
World.getWorld().schedule(new SkillNormalizationTask(this));
}
}
+45 -17
View File
@@ -1,6 +1,11 @@
package org.apollo.game.model.entity;
import java.util.Arrays;
import java.util.Optional;
import org.apollo.game.model.Position;
import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.game.sync.block.SynchronizationBlock;
@@ -14,11 +19,6 @@ import com.google.common.base.Preconditions;
*/
public final class Npc extends Mob {
/**
* This npc's id.
*/
private int id;
/**
* The positions representing the bounds (i.e. walking limits) of this npc.
*/
@@ -31,19 +31,29 @@ public final class Npc extends Mob {
* @param position The position.
*/
public Npc(int id, Position position) {
this(NpcDefinition.lookup(id), position);
this(position, NpcDefinition.lookup(id));
}
/**
* Creates a new npc with the specified {@link NpcDefinition} and {@link Position}.
*
* @param definition The definition.
* @param position The position.
* @param definition The definition.
*/
public Npc(NpcDefinition definition, Position position) {
super(position);
this.definition = definition;
this.id = definition.getId();
public Npc(Position position, NpcDefinition definition) {
super(position, definition);
init();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Npc) {
Npc other = (Npc) obj;
return position.equals(other.position) && Arrays.equals(boundary, other.boundary) && getId() == other.getId();
}
return false;
}
/**
@@ -52,7 +62,7 @@ public final class Npc extends Mob {
* @return The boundary.
*/
public Position[] getBoundary() {
return boundary;
return boundary.clone();
}
@Override
@@ -66,7 +76,14 @@ public final class Npc extends Mob {
* @return The id.
*/
public int getId() {
return id;
return definition.get().getId();
}
@Override
public int hashCode() {
final int prime = 31;
int result = prime * position.hashCode() + Arrays.hashCode(boundary);
return prime * result + getId();
}
/**
@@ -85,12 +102,12 @@ public final class Npc extends Mob {
*/
public void setBoundary(Position[] boundary) {
Preconditions.checkArgument(boundary.length == 4, "Boundary count must be 4.");
this.boundary = boundary;
this.boundary = boundary.clone();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("id", definition.getId()).add("name", definition.getName()).toString();
return MoreObjects.toStringHelper(this).add("id", getId()).add("name", definition.get().getName()).toString();
}
/**
@@ -99,9 +116,20 @@ public final class Npc extends Mob {
* @param id The id.
*/
public void transform(int id) {
Preconditions.checkArgument(id >= 0 && id < NpcDefinition.count(), "Id to transform to is out of bounds.");
definition = NpcDefinition.lookup(this.id = id);
Preconditions.checkElementIndex(id, NpcDefinition.count(), "Id to transform to is out of bounds.");
definition = Optional.of(NpcDefinition.lookup(id));
blockSet.add(SynchronizationBlock.createTransformBlock(id));
}
/**
* Initialises this npc.
*/
private void init() {
// This has to be here instead of in Mob#init because of ordering issues - the player cannot be added to the
// sector until their credentials have been set, which is only done after the super constructors are called.
Sector sector = World.getWorld().getSectorRepository().get(position.getSectorCoordinates());
sector.addEntity(this);
}
}
@@ -18,6 +18,7 @@ import org.apollo.game.message.impl.UpdateRunEnergyMessage;
import org.apollo.game.model.Appearance;
import org.apollo.game.model.Position;
import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.inter.InterfaceConstants;
import org.apollo.game.model.inter.InterfaceListener;
import org.apollo.game.model.inter.InterfaceSet;
@@ -82,6 +83,21 @@ public final class Player extends Mob {
*/
private PlayerCredentials credentials;
@Override
public int hashCode() {
return credentials.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Player) {
Player other = (Player) obj;
return credentials.equals(other.credentials);
}
return false;
}
/**
* A flag which indicates there are npcs that couldn't be added.
*/
@@ -206,6 +222,7 @@ public final class Player extends Mob {
public Player(PlayerCredentials credentials, Position position) {
super(position);
this.credentials = credentials;
init();
}
@@ -923,6 +940,11 @@ public final class Player extends Mob {
private void init() {
initInventories();
initSkills();
// This has to be here instead of in Mob#init because of ordering issues - the player cannot be added to the
// sector until their credentials have been set, which is only done after the super constructors are called.
Sector sector = World.getWorld().getSectorRepository().get(position.getSectorCoordinates());
sector.addEntity(this);
}
/**
@@ -7,18 +7,23 @@ package org.apollo.game.sync.block;
*/
public final class InteractingMobBlock extends SynchronizationBlock {
/**
* The index used to reset the interacting mob.
*/
public static final int RESET_INDEX = 65_535;
/**
* The index of the mob.
*/
private final int mobIndex;
private final int index;
/**
* Creates the interacting mob block.
*
* @param mobIndex The index of the current interacting mob.
* @param index The index of the current interacting mob.
*/
InteractingMobBlock(int mobIndex) {
this.mobIndex = mobIndex;
InteractingMobBlock(int index) {
this.index = index;
}
/**
@@ -26,8 +31,8 @@ public final class InteractingMobBlock extends SynchronizationBlock {
*
* @return The index.
*/
public int getInteractingMobIndex() {
return mobIndex;
public int getIndex() {
return index;
}
}
@@ -34,9 +34,11 @@ public abstract class SynchronizationBlock {
* @return The appearance block.
*/
public static SynchronizationBlock createAppearanceBlock(Player player) {
return new AppearanceBlock(player.getEncodedName(), player.getAppearance(), player.getSkillSet().getCombatLevel(), 0,
player.getEquipment(), player.getPrayerIcon(), player.isSkulled(), player.getDefinition() == null ? -1 : player
.getDefinition().getId());
int combat = player.getSkillSet().getCombatLevel();
int id = player.hasNpcDefinition() ? player.getDefinition().getId() : -1;
return new AppearanceBlock(player.getEncodedName(), player.getAppearance(), combat, 0, player.getEquipment(),
player.getPrayerIcon(), player.isSkulled(), id);
}
/**
@@ -38,9 +38,12 @@ public final class PhasedSynchronizationTask extends SynchronizationTask {
public void run() {
try {
task.run();
} catch (Exception e) { // TODO better solution...
e.printStackTrace();
// The executor suppresses any exceptions thrown as part of the task, so we catch and print here as
// rethrowing them does nothing.
} finally {
phaser.arriveAndDeregister();
}
}
}
@@ -53,7 +53,6 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
Position position = player.getPosition();
player.setLastKnownSector(position);
player.send(new SectorChangeMessage(position));
}
}
@@ -19,19 +19,21 @@ public final class MouseClickMessageDecoder extends MessageDecoder<MouseClickMes
int read;
if (reader.getLength() == 2) {
read = (int) reader.getUnsigned(DataType.SHORT);
int clickCount = (read >> 12);
int clicks = (read >> 12);
int dX = (read >> 6) & 0x3f;
int dY = read & 0x3f;
return new MouseClickMessage(clickCount, dX, dY, true);
return new MouseClickMessage(clicks, dX, dY, true);
} else if (reader.getLength() == 3) {
read = (int) reader.getUnsigned(DataType.TRI_BYTE) & ~0x800000;
} else {
read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000;
}
int clickCount = (read >> 19);
int clicks = (read >> 19);
int x = (read & 0x7f) % 765;
int y = (read & 0x7f) / 765;
return new MouseClickMessage(clickCount, x, y, false);
return new MouseClickMessage(clicks, x, y, false);
}
}
@@ -217,7 +217,7 @@ public final class NpcSynchronizationMessageEncoder extends MessageEncoder<NpcSy
* @param builder The builder.
*/
private static void putInteractingMobBlock(InteractingMobBlock block, GamePacketBuilder builder) {
builder.put(DataType.SHORT, block.getInteractingMobIndex());
builder.put(DataType.SHORT, block.getIndex());
}
/**
@@ -378,7 +378,7 @@ public final class PlayerSynchronizationMessageEncoder extends MessageEncoder<Pl
* @param builder The builder.
*/
private static void putInteractingMobBlock(InteractingMobBlock block, GamePacketBuilder builder) {
builder.put(DataType.SHORT, DataOrder.LITTLE, block.getInteractingMobIndex());
builder.put(DataType.SHORT, DataOrder.LITTLE, block.getIndex());
}
/**
@@ -19,8 +19,8 @@ import org.apollo.game.message.impl.OpenInterfaceSidebarMessage;
import org.apollo.game.message.impl.PlayerSynchronizationMessage;
import org.apollo.game.message.impl.PositionMessage;
import org.apollo.game.message.impl.PrivacyOptionMessage;
import org.apollo.game.message.impl.SectorChangeMessage;
import org.apollo.game.message.impl.RemoveTileItemMessage;
import org.apollo.game.message.impl.SectorChangeMessage;
import org.apollo.game.message.impl.SendFriendMessage;
import org.apollo.game.message.impl.SendObjectMessage;
import org.apollo.game.message.impl.ServerChatMessage;
@@ -128,7 +128,7 @@ public final class Release317 extends Release {
register(237, new MagicOnItemMessageDecoder());
register(3, new FocusUpdateMessageDecoder());
register(241, new MouseClickMessageDecoder());
register(45, new MouseClickMessageDecoder());
register(86, new ArrowKeyMessageDecoder());
register(95, new PrivacyOptionMessageDecoder());
@@ -16,23 +16,24 @@ public final class MouseClickMessageDecoder extends MessageDecoder<MouseClickMes
@Override
public MouseClickMessage decode(GamePacket packet) {
GamePacketReader reader = new GamePacketReader(packet);
int read, clickCount, x, y;
int read, clicks, x, y;
if (reader.getLength() == 2) {
read = (int) reader.getUnsigned(DataType.SHORT);
clickCount = (read >> 12);
clicks = (read >> 12);
x = (read >> 6) & 0x3f;
y = read & 0x3f;
return new MouseClickMessage(clickCount, x, y, true);
return new MouseClickMessage(clicks, x, y, true);
} else if (reader.getLength() == 3) {
read = (int) reader.getUnsigned(DataType.TRI_BYTE) & ~0x800000;
} else {
read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000;
}
clickCount = (read >> 19);
clicks = (read >> 19);
x = (read & 0x7f) % 765;
y = (read & 0x7f) / 765;
return new MouseClickMessage(clickCount, x, y, false);
return new MouseClickMessage(clicks, x, y, false);
}
}
@@ -217,7 +217,7 @@ public final class NpcSynchronizationMessageEncoder extends MessageEncoder<NpcSy
* @param builder The builder.
*/
private static void putInteractingMobBlock(InteractingMobBlock block, GamePacketBuilder builder) {
builder.put(DataType.SHORT, DataOrder.LITTLE, block.getInteractingMobIndex());
builder.put(DataType.SHORT, DataOrder.LITTLE, block.getIndex());
}
/**
@@ -378,7 +378,7 @@ public final class PlayerSynchronizationMessageEncoder extends MessageEncoder<Pl
* @param builder The builder.
*/
private static void putInteractingMobBlock(InteractingMobBlock block, GamePacketBuilder builder) {
builder.put(DataType.SHORT, DataTransformation.ADD, block.getInteractingMobIndex());
builder.put(DataType.SHORT, DataTransformation.ADD, block.getIndex());
}
/**
@@ -106,19 +106,17 @@ public final class PlayerCredentials {
@Override
public int hashCode() {
return (int) encodedUsername;
return Long.hashCode(encodedUsername);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
if (obj instanceof PlayerCredentials) {
PlayerCredentials other = (PlayerCredentials) obj;
return encodedUsername == other.encodedUsername;
}
PlayerCredentials other = (PlayerCredentials) obj;
return encodedUsername == other.encodedUsername;
return false;
}
}