Improve attributes code.

This commit is contained in:
Major-
2015-03-02 03:57:15 +00:00
parent d74d2fc987
commit 03c1fe16ac
14 changed files with 217 additions and 149 deletions
@@ -16,7 +16,6 @@ public final class PlayerDesignMessageHandler extends MessageHandler<PlayerDesig
@Override
public void handle(MessageHandlerContext ctx, Player player, PlayerDesignMessage message) {
player.setAppearance(message.getAppearance());
player.setNew(false);
player.send(new CloseInterfaceMessage());
}
+2 -2
View File
@@ -157,7 +157,7 @@ public abstract class Mob extends Entity {
* @return The value of the attribute.
*/
public final Attribute<?> getAttribute(String name) {
return attributes.getAttribute(name);
return attributes.get(name);
}
/**
@@ -369,7 +369,7 @@ public abstract class Mob extends Entity {
* @param value The attribute.
*/
public final void setAttribute(String name, Attribute<?> value) {
attributes.setAttribute(name, value);
attributes.set(name, value);
}
/**
+25 -50
View File
@@ -19,6 +19,11 @@ 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.entity.attr.Attribute;
import org.apollo.game.model.entity.attr.AttributeDefinition;
import org.apollo.game.model.entity.attr.AttributeMap;
import org.apollo.game.model.entity.attr.AttributePersistence;
import org.apollo.game.model.entity.attr.NumericalAttribute;
import org.apollo.game.model.entity.setting.MembershipStatus;
import org.apollo.game.model.entity.setting.PrivacyState;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
@@ -55,6 +60,11 @@ import com.google.common.base.Preconditions;
*/
public final class Player extends Mob {
static {
AttributeMap.define("run_energy", AttributeDefinition.forInt(100, AttributePersistence.PERSISTENT));
AttributeMap.define("client_version", AttributeDefinition.forInt(0, AttributePersistence.TRANSIENT));
}
/**
* The player's appearance.
*/
@@ -75,12 +85,6 @@ public final class Player extends Mob {
*/
private transient Deque<Point> clicks = new ArrayDeque<>();
/**
* The version of the client this player is using. This is not the same as the release number, instead denoting the
* custom version.
*/
private transient int clientVersion;
/**
* This player's credentials.
*/
@@ -136,11 +140,6 @@ public final class Player extends Mob {
*/
private transient MembershipStatus members = MembershipStatus.FREE;
/**
* A flag indicating if the player is new.
*/
private boolean newPlayer = false;
/**
* This player's prayer icon.
*/
@@ -156,11 +155,6 @@ public final class Player extends Mob {
*/
private final transient Deque<Message> queuedMessages = new ArrayDeque<>();
/**
* The player's run energy.
*/
private int runEnergy = 100;
/**
* A flag indicating if this player is running.
*/
@@ -327,7 +321,8 @@ public final class Player extends Mob {
* @return The version.
*/
public int getClientVersion() {
return clientVersion;
Attribute<Integer> version = attributes.get("client_version");
return version.getValue();
}
/**
@@ -436,7 +431,8 @@ public final class Player extends Mob {
* @return The run energy.
*/
public int getRunEnergy() {
return runEnergy;
Attribute<Integer> energy = attributes.get("run_energy");
return energy.getValue();
}
/**
@@ -571,15 +567,6 @@ public final class Player extends Mob {
return members;
}
/**
* Checks if this player has logged in before.
*
* @return A flag indicating if the player is new.
*/
public boolean isNew() {
return newPlayer;
}
/**
* Checks if this player is running.
*
@@ -701,9 +688,6 @@ public final class Player extends Mob {
blockSet.add(SynchronizationBlock.createAppearanceBlock(this));
send(new IdAssignmentMessage(index, members)); // TODO should this be sent when we reconnect?
sendMessage("Welcome to RuneScape.");
if (newPlayer) {
interfaceSet.openWindow(InterfaceConstants.AVATAR_DESIGN);
}
int[] tabs = InterfaceConstants.DEFAULT_INVENTORY_TABS;
for (int tab = 0; tab < tabs.length; tab++) {
@@ -734,7 +718,7 @@ public final class Player extends Mob {
* @param filterable Whether or not the message can be filtered.
*/
public void sendMessage(String message, boolean filterable) {
if (clientVersion > 0) {
if (getClientVersion() > 0) {
send(new ServerChatMessage(message, filterable));
} else if (!filterable || !filteringMessages) {
send(new ServerChatMessage(message));
@@ -791,12 +775,12 @@ public final class Player extends Mob {
}
/**
* Sets the value denoting the client's modified version. TODO make this an attribute?
* Sets the value denoting the client's modified version.
*
* @param clientVersion The client version.
* @param version The client version.
*/
public void setClientVersion(int clientVersion) {
this.clientVersion = clientVersion;
public void setClientVersion(int version) {
attributes.set("client_version", new NumericalAttribute(version));
}
/**
@@ -844,15 +828,6 @@ public final class Player extends Mob {
this.members = members;
}
/**
* Sets the new player flag. TODO make this an attribute?
*
* @param newPlayer A flag indicating if the player has played before.
*/
public void setNew(boolean newPlayer) {
this.newPlayer = newPlayer;
}
/**
* Sets the player's prayer icon. TODO make this an attribute?
*
@@ -872,13 +847,13 @@ public final class Player extends Mob {
}
/**
* Sets the player's run energy. TODO make this an attribute?
* Sets the player's run energy.
*
* @param runEnergy The energy.
* @param energy The energy.
*/
public void setRunEnergy(int runEnergy) {
this.runEnergy = runEnergy;
send(new UpdateRunEnergyMessage(runEnergy));
public void setRunEnergy(int energy) {
attributes.set("run_energy", new NumericalAttribute(energy));
send(new UpdateRunEnergyMessage(energy));
}
/**
@@ -969,7 +944,7 @@ public final class Player extends Mob {
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("username", getUsername()).add("privilege", privilegeLevel)
.add("client version", clientVersion).toString();
.add("client version", getClientVersion()).toString();
}
/**
@@ -13,12 +13,12 @@ public abstract class Attribute<T> {
/**
* The type of this attribute.
*/
private final AttributeType type;
protected final AttributeType type;
/**
* The value of this attribute.
*/
protected T value;
protected final T value;
/**
* Creates the attribute with the specified {@link AttributeType} and value.
@@ -31,6 +31,13 @@ public abstract class Attribute<T> {
this.value = value;
}
/**
* Encodes this Attribute into a byte array.
*
* @return The byte array.
*/
public abstract byte[] encode();
/**
* Gets the type of this attribute.
*
@@ -49,4 +56,11 @@ public abstract class Attribute<T> {
return value;
}
/**
* Returns the Sting representation of this Attribute. Will be used to write this Attribute as a String, if
* required.
*/
@Override
public abstract String toString();
}
@@ -10,25 +10,69 @@ package org.apollo.game.model.entity.attr;
public final class AttributeDefinition<T> {
/**
* The default value of this definition.
* Creates an AttributeDefinition for a {@code boolean}.
*
* @param defaultValue The default value of the definition.
* @param persistence The {@link AttributePersistence} of the definition.
* @return The AttributeDefinition.
*/
public static AttributeDefinition<Boolean> forBoolean(boolean defaultValue, AttributePersistence persistence) {
return new AttributeDefinition<>(defaultValue, persistence, AttributeType.BOOLEAN);
}
/**
* Creates an AttributeDefinition for a {@code double}.
*
* @param defaultValue The default value of the definition.
* @param persistence The {@link AttributePersistence} of the definition.
* @return The AttributeDefinition.
*/
public static AttributeDefinition<Double> forDouble(double defaultValue, AttributePersistence persistence) {
return new AttributeDefinition<>(defaultValue, persistence, AttributeType.DOUBLE);
}
/**
* Creates an AttributeDefinition for an {@code int}.
*
* @param defaultValue The default value of the definition.
* @param persistence The {@link AttributePersistence} of the definition.
* @return The AttributeDefinition.
*/
public static AttributeDefinition<Integer> forInt(int defaultValue, AttributePersistence persistence) {
return new AttributeDefinition<>(defaultValue, persistence, AttributeType.LONG);
}
/**
* Creates an AttributeDefinition for a String.
*
* @param defaultValue The default value of the definition.
* @param persistence The {@link AttributePersistence} of the definition.
* @return The AttributeDefinition.
*/
public static AttributeDefinition<String> forString(String defaultValue, AttributePersistence persistence) {
return new AttributeDefinition<>(defaultValue, persistence, AttributeType.STRING);
}
/**
* The default value of the Attribute.
*/
private final T defaultValue;
/**
* The persistence state of this definition.
* The persistence state of the Attribute.
*/
private final AttributePersistence persistence;
/**
* The type of this definition.
* The type of the Attribute.
*/
private final AttributeType type;
/**
* Creates the attribute definition.
* Creates the AttributeDefinition.
*
* @param defaultValue The default value.
* @param persistence The {@link AttributePersistence} state.
* @param persistence The {@link AttributePersistence}.
* @param type The {@link AttributeType}.
*/
public AttributeDefinition(T defaultValue, AttributePersistence persistence, AttributeType type) {
@@ -38,7 +82,7 @@ public final class AttributeDefinition<T> {
}
/**
* Gets the default value of this attribute definition.
* Gets the default value of this AttributeDefinition.
*
* @return The default value.
*/
@@ -47,18 +91,18 @@ public final class AttributeDefinition<T> {
}
/**
* Gets the persistence state of this attribute definition.
* Gets the {@link AttributePersistence} of this AttributeDefinition.
*
* @return The persistence.
* @return The AttributePersistence.
*/
public AttributePersistence getPersistence() {
return persistence;
}
/**
* Gets the {@link AttributeType} of this definition.
* Gets the {@link AttributeType} of this AttributeDefinition
*
* @return The attribute type.
* @return The AttributeType.
*/
public AttributeType getType() {
return type;
@@ -3,6 +3,8 @@ package org.apollo.game.model.entity.attr;
import java.util.HashMap;
import java.util.Map;
import org.jruby.RubySymbol;
import com.google.common.base.Preconditions;
/**
@@ -12,10 +14,15 @@ import com.google.common.base.Preconditions;
*/
public final class AttributeMap {
/**
* The default size of the map.
*/
private static final int DEFAULT_MAP_SIZE = 2;
/**
* The map of attribute names to definitions.
*/
private static Map<String, AttributeDefinition<?>> definitions = new HashMap<>(1);
private static Map<String, AttributeDefinition<?>> definitions = new HashMap<>();
/**
* Registers an {@link AttributeDefinition}.
@@ -23,7 +30,7 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @param definition The definition.
*/
public static void addDefinition(String name, AttributeDefinition<?> definition) {
public static void define(String name, AttributeDefinition<?> definition) {
definitions.put(name, definition);
}
@@ -33,8 +40,9 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @return The attribute definition.
*/
public static AttributeDefinition<?> getDefinition(String name) {
return definitions.get(name);
@SuppressWarnings("unchecked")
public static <T> AttributeDefinition<T> getDefinition(String name) {
return (AttributeDefinition<T>) definitions.get(name);
}
/**
@@ -46,10 +54,20 @@ public final class AttributeMap {
return new HashMap<>(definitions);
}
/**
* Returns whether or not an {@link AttributeDefinition} with the specified name exists.
*
* @param name The name of the AttributeDefinition.
* @return {@code true} if the AttributeDefinition exists, {@code false} if not.
*/
public static boolean hasDefinition(String name) {
return definitions.containsKey(name);
}
/**
* The map of attribute names to attributes.
*/
private Map<String, Attribute<?>> attributes = new HashMap<>();
private Map<String, Attribute<?>> attributes = new HashMap<>(DEFAULT_MAP_SIZE);
/**
* Gets the {@link Attribute} with the specified name.
@@ -57,8 +75,13 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @return The attribute.
*/
public Attribute<?> getAttribute(String name) {
return attributes.get(name);
@SuppressWarnings("unchecked")
public <T> Attribute<T> get(String name) {
AttributeDefinition<T> definition = getDefinition(name);
Preconditions.checkNotNull(definition, "Attributes must be defined before their value can be retreived.");
return (Attribute<T>) attributes.computeIfAbsent(name,
key -> createAttribute(definition.getDefault(), definition.getType()));
}
/**
@@ -76,9 +99,32 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @param attribute The attribute.
*/
public void setAttribute(String name, Attribute<?> attribute) {
public void set(String name, Attribute<?> attribute) {
Preconditions.checkNotNull(getDefinition(name), "Attributes must be defined before their value can be set.");
attributes.put(name, attribute);
}
/**
* Creates an {@link Attribute} with the specified value and {@link AttributeType}.
*
* @param value The value of the Attribute.
* @param type The AttributeType.
* @return The Attribute.
*/
private <T> Attribute<?> createAttribute(T value, AttributeType type) {
switch (type) {
case LONG:
case DOUBLE:
return new NumericalAttribute((Integer) value);
case STRING:
return new StringAttribute((String) value);
case SYMBOL:
return new StringAttribute(((RubySymbol) value).asJavaString(), true);
case BOOLEAN:
return new BooleanAttribute((Boolean) value);
}
throw new IllegalArgumentException("Unrecognised type " + type + ".");
}
}
@@ -8,7 +8,7 @@ public enum AttributePersistence {
/**
* The serialized persistence type, indicating that the attribute will be saved.
*/
SERIALIZED,
PERSISTENT,
/**
* The transient persistence type, indicating that the attribute will not be saved.
@@ -16,4 +16,14 @@ public final class BooleanAttribute extends Attribute<Boolean> {
super(AttributeType.BOOLEAN, value);
}
@Override
public byte[] encode() {
return new byte[] { (byte) (value ? 1 : 0) };
}
@Override
public String toString() {
return Boolean.toString(value);
}
}
@@ -1,5 +1,7 @@
package org.apollo.game.model.entity.attr;
import com.google.common.primitives.Longs;
/**
* An {@link Attribute} with a numerical value.
*
@@ -26,4 +28,15 @@ public final class NumericalAttribute extends Attribute<Number> {
super(typeOf(value), value);
}
@Override
public byte[] encode() {
long encoded = (type == AttributeType.DOUBLE) ? Double.doubleToLongBits((double) value) : (long) value;
return Longs.toByteArray(encoded);
}
@Override
public String toString() {
return (type == AttributeType.DOUBLE) ? Double.toString((double) value) : Long.toString((long) value);
}
}
@@ -1,5 +1,7 @@
package org.apollo.game.model.entity.attr;
import java.nio.charset.Charset;
/**
* An {@link Attribute} with a string value.
*
@@ -26,4 +28,14 @@ public final class StringAttribute extends Attribute<String> {
super(symbol ? AttributeType.SYMBOL : AttributeType.STRING, value);
}
@Override
public byte[] encode() {
return value.getBytes(Charset.forName("UTF-8"));
}
@Override
public String toString() {
return value;
}
}
@@ -58,35 +58,28 @@ public final class BinaryPlayerLoader implements PlayerLoader {
}
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
// read credentials and privileges
String name = StreamUtil.readString(in);
String pass = StreamUtil.readString(in);
String password = StreamUtil.readString(in);
if (!name.equalsIgnoreCase(credentials.getUsername()) || !SCryptUtil.check(credentials.getPassword(), pass)) {
if (!name.equalsIgnoreCase(credentials.getUsername()) || !SCryptUtil.check(credentials.getPassword(), password)) {
return new PlayerLoaderResponse(LoginConstants.STATUS_INVALID_CREDENTIALS);
}
// set the credentials password to the scrypted one
credentials.setPassword(pass);
credentials.setPassword(password); // Update password to the hashed one.
PrivilegeLevel privilegeLevel = PrivilegeLevel.valueOf(in.readByte());
MembershipStatus members = MembershipStatus.valueOf(in.readByte());
// read settings
PrivacyState chatPrivacy = PrivacyState.valueOf(in.readByte(), true);
PrivacyState friendPrivacy = PrivacyState.valueOf(in.readByte(), false);
PrivacyState tradePrivacy = PrivacyState.valueOf(in.readByte(), false);
int runEnergy = in.readByte();
ScreenBrightness brightness = ScreenBrightness.valueOf(in.readByte());
// read position
int x = in.readUnsignedShort();
int y = in.readUnsignedShort();
int height = in.readUnsignedByte();
// read appearance
boolean designed = in.readBoolean();
int genderIntValue = in.readUnsignedByte();
Gender gender = genderIntValue == Gender.MALE.toInteger() ? Gender.MALE : Gender.FEMALE;
int[] style = new int[7];
@@ -107,15 +100,12 @@ public final class BinaryPlayerLoader implements PlayerLoader {
player.setRunEnergy(runEnergy);
player.setScreenBrightness(brightness);
player.setNew(designed);
player.setAppearance(new Appearance(gender, style, colors));
// read inventories
readInventory(in, player.getInventory());
readInventory(in, player.getEquipment());
readInventory(in, player.getBank());
// read skills
int size = in.readUnsignedByte();
SkillSet skills = player.getSkillSet();
skills.stopFiringEvents();
@@ -139,7 +129,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
int ignoreCount = in.readByte();
List<String> ignores = new ArrayList<>(ignoreCount);
for (int i = 0; i < ignoreCount; i++) {
for (int times = 0; times < ignoreCount; times++) {
ignores.add(NameUtil.decodeBase37(in.readLong()));
}
player.setIgnoredUsernames(ignores);
@@ -161,11 +151,12 @@ public final class BinaryPlayerLoader implements PlayerLoader {
private static Map<String, Attribute<?>> readAttributes(DataInputStream in) throws IOException {
int count = in.readInt();
Map<String, Attribute<?>> attributes = new HashMap<>(count);
Attribute<?> attribute;
for (int i = 0; i < count; i++) {
for (int times = 0; times < count; times++) {
String name = StreamUtil.readString(in);
AttributeType type = AttributeType.valueOf(in.read());
Attribute<?> attribute;
switch (type) {
case BOOLEAN:
attribute = new BooleanAttribute(in.read() == 1);
@@ -17,7 +17,6 @@ import org.apollo.game.model.entity.SkillSet;
import org.apollo.game.model.entity.attr.Attribute;
import org.apollo.game.model.entity.attr.AttributeMap;
import org.apollo.game.model.entity.attr.AttributePersistence;
import org.apollo.game.model.entity.attr.AttributeType;
import org.apollo.game.model.inv.Inventory;
import org.apollo.io.player.PlayerSaver;
import org.apollo.util.NameUtil;
@@ -35,27 +34,22 @@ public final class BinaryPlayerSaver implements PlayerSaver {
File file = BinaryPlayerUtil.getFile(player.getUsername());
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
// write credentials and privileges
StreamUtil.writeString(out, player.getUsername());
StreamUtil.writeString(out, player.getCredentials().getPassword());
out.writeByte(player.getPrivilegeLevel().toInteger());
out.writeByte(player.getMembershipStatus().getValue());
// write settings
out.writeByte(player.getChatPrivacy().toInteger(true));
out.writeByte(player.getFriendPrivacy().toInteger(false));
out.writeByte(player.getTradePrivacy().toInteger(false));
out.writeByte(player.getRunEnergy());
out.writeByte(player.getScreenBrightness().toInteger());
// write position
Position position = player.getPosition();
out.writeShort(position.getX());
out.writeShort(position.getY());
out.writeByte(position.getHeight());
// write appearance
out.writeBoolean(player.isNew());
Appearance appearance = player.getAppearance();
out.writeByte(appearance.getGender().toInteger());
int[] style = appearance.getStyle();
@@ -66,14 +60,11 @@ public final class BinaryPlayerSaver implements PlayerSaver {
for (int color : colors) {
out.writeByte(color);
}
out.flush();
// write inventories
writeInventory(out, player.getInventory());
writeInventory(out, player.getEquipment());
writeInventory(out, player.getBank());
// write skills
SkillSet skills = player.getSkillSet();
out.writeByte(skills.size());
for (int id = 0; id < skills.size(); id++) {
@@ -95,47 +86,20 @@ public final class BinaryPlayerSaver implements PlayerSaver {
}
Set<Entry<String, Attribute<?>>> attributes = player.getAttributes().entrySet();
attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.SERIALIZED);
attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.PERSISTENT);
out.writeInt(attributes.size());
for (Entry<String, Attribute<?>> entry : attributes) {
String name = entry.getKey();
StreamUtil.writeString(out, name);
saveAttribute(out, entry.getValue());
Attribute<?> attribute = entry.getValue();
out.writeByte(attribute.getType().getValue());
out.write(attribute.encode());
}
}
}
/**
* Writes an {@link Attribute} to the specified output stream.
*
* @param out The output stream.
* @param attribute The attribute.
* @throws IOException If an I/O error occurs.
*/
private static void saveAttribute(DataOutputStream out, Attribute<?> attribute) throws IOException {
AttributeType type = attribute.getType();
out.writeByte(type.getValue());
switch (type) {
case BOOLEAN:
out.writeByte((Boolean) attribute.getValue() ? 1 : 0);
break;
case DOUBLE:
out.writeDouble((Double) attribute.getValue());
break;
case LONG:
out.writeLong((Long) attribute.getValue());
break;
case STRING:
case SYMBOL:
StreamUtil.writeString(out, (String) attribute.getValue());
break;
default:
throw new IllegalArgumentException("Undefined attribute type " + type + ".");
}
}
/**
* Writes an inventory to the specified output stream.
*
@@ -32,8 +32,8 @@ public final class BinaryPlayerUtil {
* @return The file.
*/
public static File getFile(String username) {
username = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
return new File(SAVED_GAMES_DIRECTORY, username + ".dat");
String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
return new File(SAVED_GAMES_DIRECTORY, filtered + ".dat");
}
/**