mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
Merge PlayerSaver and PlayerLoader into PlayerSerializer.
This commit is contained in:
+1
-2
@@ -1,4 +1,3 @@
|
||||
<login>
|
||||
<loader>org.apollo.io.player.impl.DummyPlayerLoader</loader>
|
||||
<saver>org.apollo.io.player.impl.DiscardPlayerSaver</saver>
|
||||
<serializer>org.apollo.io.player.DummyPlayerSerializer</serializer>
|
||||
</login>
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.apollo.io.player;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apollo.util.NameUtil;
|
||||
|
||||
/**
|
||||
* A utility class with common functionality used by the binary player loader/ savers.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public final class BinaryFileUtils {
|
||||
|
||||
/**
|
||||
* The Path to the saved games directory.
|
||||
*/
|
||||
private static final Path SAVED_GAMES_DIRECTORY = Paths.get("data/savedGames");
|
||||
|
||||
/**
|
||||
* Creates the saved games directory if it does not exist.
|
||||
*/
|
||||
static {
|
||||
try {
|
||||
if (!Files.exists(SAVED_GAMES_DIRECTORY)) {
|
||||
Files.createDirectory(SAVED_GAMES_DIRECTORY);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error creating saved games directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the save {@link File} for the specified player.
|
||||
*
|
||||
* @param username The username of the player.
|
||||
* @return The file.
|
||||
*/
|
||||
public static Path getFile(String username) {
|
||||
String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
|
||||
return SAVED_GAMES_DIRECTORY.resolve(filtered + ".dat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sole private constructor to prevent instantiation.
|
||||
*/
|
||||
private BinaryFileUtils() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+145
-25
@@ -1,13 +1,20 @@
|
||||
package org.apollo.io.player.impl;
|
||||
package org.apollo.io.player;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apollo.game.model.Appearance;
|
||||
import org.apollo.game.model.Item;
|
||||
@@ -16,6 +23,8 @@ import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.game.model.entity.Skill;
|
||||
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.entity.attr.BooleanAttribute;
|
||||
import org.apollo.game.model.entity.attr.NumericalAttribute;
|
||||
@@ -26,8 +35,6 @@ import org.apollo.game.model.entity.setting.PrivacyState;
|
||||
import org.apollo.game.model.entity.setting.PrivilegeLevel;
|
||||
import org.apollo.game.model.entity.setting.ScreenBrightness;
|
||||
import org.apollo.game.model.inv.Inventory;
|
||||
import org.apollo.io.player.PlayerLoader;
|
||||
import org.apollo.io.player.PlayerLoaderResponse;
|
||||
import org.apollo.net.codec.login.LoginConstants;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
import org.apollo.util.NameUtil;
|
||||
@@ -36,28 +43,39 @@ import org.apollo.util.StreamUtil;
|
||||
import com.lambdaworks.crypto.SCryptUtil;
|
||||
|
||||
/**
|
||||
* A {@link PlayerLoader} implementation that loads data from a binary file.
|
||||
* A {@link PlayerSerializer} implementation that uses a binary file to store player data.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
public final class BinaryPlayerSerializer implements PlayerSerializer {
|
||||
|
||||
/**
|
||||
* The default spawn position.
|
||||
* The Path to the saved games directory.
|
||||
*/
|
||||
private static final Position SPAWN_POSITION = new Position(3093, 3104);
|
||||
private static final Path SAVED_GAMES_DIRECTORY = Paths.get("data/savedGames");
|
||||
|
||||
static {
|
||||
try {
|
||||
if (!Files.exists(SAVED_GAMES_DIRECTORY)) {
|
||||
Files.createDirectory(SAVED_GAMES_DIRECTORY);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error creating saved games directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws IOException {
|
||||
File file = BinaryPlayerUtil.getFile(credentials.getUsername());
|
||||
if (!file.exists()) {
|
||||
Player player = new Player(credentials, SPAWN_POSITION);
|
||||
player.getBank().add(995, 25); // 25 coins
|
||||
Path path = getFile(credentials.getUsername());
|
||||
if (!Files.exists(path)) {
|
||||
Player player = new Player(credentials, TUTORIAL_ISLAND_SPAWN);
|
||||
|
||||
credentials.setPassword(SCryptUtil.scrypt(credentials.getPassword(), 16384, 8, 1));
|
||||
return new PlayerLoaderResponse(LoginConstants.STATUS_OK, player);
|
||||
}
|
||||
|
||||
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
|
||||
try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path)))) {
|
||||
String name = StreamUtil.readString(in);
|
||||
String password = StreamUtil.readString(in);
|
||||
|
||||
@@ -67,37 +85,35 @@ public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
|
||||
credentials.setPassword(password); // Update password to the hashed one.
|
||||
|
||||
PrivilegeLevel privilegeLevel = PrivilegeLevel.valueOf(in.readByte());
|
||||
PrivilegeLevel privilege = PrivilegeLevel.valueOf(in.readByte());
|
||||
MembershipStatus members = MembershipStatus.valueOf(in.readByte());
|
||||
|
||||
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());
|
||||
|
||||
int x = in.readUnsignedShort();
|
||||
int y = in.readUnsignedShort();
|
||||
int height = in.readUnsignedByte();
|
||||
|
||||
int genderIntValue = in.readUnsignedByte();
|
||||
Gender gender = genderIntValue == Gender.MALE.toInteger() ? Gender.MALE : Gender.FEMALE;
|
||||
Gender gender = (in.readUnsignedByte() == Gender.MALE.toInteger()) ? Gender.MALE : Gender.FEMALE;
|
||||
int[] style = new int[7];
|
||||
for (int i = 0; i < style.length; i++) {
|
||||
style[i] = in.readUnsignedByte();
|
||||
for (int slot = 0; slot < style.length; slot++) {
|
||||
style[slot] = in.readUnsignedByte();
|
||||
}
|
||||
|
||||
int[] colors = new int[5];
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
colors[i] = in.readUnsignedByte();
|
||||
for (int slot = 0; slot < colors.length; slot++) {
|
||||
colors[slot] = in.readUnsignedByte();
|
||||
}
|
||||
|
||||
Player player = new Player(credentials, new Position(x, y, height));
|
||||
player.setPrivilegeLevel(privilegeLevel);
|
||||
player.setPrivilegeLevel(privilege);
|
||||
player.setMembers(members);
|
||||
player.setChatPrivacy(chatPrivacy);
|
||||
player.setFriendPrivacy(friendPrivacy);
|
||||
player.setTradePrivacy(tradePrivacy);
|
||||
player.setRunEnergy(runEnergy);
|
||||
player.setScreenBrightness(brightness);
|
||||
|
||||
player.setAppearance(new Appearance(gender, style, colors));
|
||||
@@ -141,6 +157,87 @@ public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) throws IOException {
|
||||
Path file = getFile(player.getUsername());
|
||||
|
||||
try (DataOutputStream out = new DataOutputStream(Files.newOutputStream(file))) {
|
||||
StreamUtil.writeString(out, player.getUsername());
|
||||
StreamUtil.writeString(out, player.getCredentials().getPassword());
|
||||
out.writeByte(player.getPrivilegeLevel().toInteger());
|
||||
out.writeByte(player.getMembershipStatus().getValue());
|
||||
|
||||
out.writeByte(player.getChatPrivacy().toInteger(true));
|
||||
out.writeByte(player.getFriendPrivacy().toInteger(false));
|
||||
out.writeByte(player.getTradePrivacy().toInteger(false));
|
||||
out.writeByte(player.getScreenBrightness().toInteger());
|
||||
|
||||
Position position = player.getPosition();
|
||||
out.writeShort(position.getX());
|
||||
out.writeShort(position.getY());
|
||||
out.writeByte(position.getHeight());
|
||||
|
||||
Appearance appearance = player.getAppearance();
|
||||
out.writeByte(appearance.getGender().toInteger());
|
||||
int[] style = appearance.getStyle();
|
||||
for (int element : style) {
|
||||
out.writeByte(element);
|
||||
}
|
||||
int[] colors = appearance.getColors();
|
||||
for (int color : colors) {
|
||||
out.writeByte(color);
|
||||
}
|
||||
|
||||
writeInventory(out, player.getInventory());
|
||||
writeInventory(out, player.getEquipment());
|
||||
writeInventory(out, player.getBank());
|
||||
|
||||
SkillSet skills = player.getSkillSet();
|
||||
out.writeByte(skills.size());
|
||||
for (int id = 0; id < skills.size(); id++) {
|
||||
Skill skill = skills.getSkill(id);
|
||||
out.writeByte(skill.getCurrentLevel());
|
||||
out.writeDouble(skill.getExperience());
|
||||
}
|
||||
|
||||
List<String> usernames = player.getFriendUsernames();
|
||||
out.writeByte(usernames.size());
|
||||
for (String username : usernames) {
|
||||
out.writeLong(NameUtil.encodeBase37(username));
|
||||
}
|
||||
|
||||
usernames = player.getIgnoredUsernames();
|
||||
out.writeByte(usernames.size());
|
||||
for (String username : usernames) {
|
||||
out.writeLong(NameUtil.encodeBase37(username));
|
||||
}
|
||||
|
||||
Set<Entry<String, Attribute<?>>> attributes = player.getAttributes().entrySet();
|
||||
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);
|
||||
|
||||
Attribute<?> attribute = entry.getValue();
|
||||
out.writeByte(attribute.getType().getValue());
|
||||
out.write(attribute.encode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the save {@link File} for the specified player.
|
||||
*
|
||||
* @param username The username of the player.
|
||||
* @return The file.
|
||||
*/
|
||||
private Path getFile(String username) {
|
||||
String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
|
||||
return SAVED_GAMES_DIRECTORY.resolve(filtered + ".dat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the player's {@link Attribute}s.
|
||||
*
|
||||
@@ -148,7 +245,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
* @return The {@link Map} of attribute names to attributes.
|
||||
* @throws IOException If there is an error reading from the stream.
|
||||
*/
|
||||
private static Map<String, Attribute<?>> readAttributes(DataInputStream in) throws IOException {
|
||||
private Map<String, Attribute<?>> readAttributes(DataInputStream in) throws IOException {
|
||||
int count = in.readInt();
|
||||
Map<String, Attribute<?>> attributes = new HashMap<>(count);
|
||||
|
||||
@@ -187,7 +284,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
* @param inventory The inventory.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private static void readInventory(DataInputStream in, Inventory inventory) throws IOException {
|
||||
private void readInventory(DataInputStream in, Inventory inventory) throws IOException {
|
||||
int capacity = in.readUnsignedShort();
|
||||
|
||||
inventory.stopFiringEvents();
|
||||
@@ -206,4 +303,27 @@ public final class BinaryPlayerLoader implements PlayerLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an inventory to the specified output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param inventory The inventory.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void writeInventory(DataOutputStream out, Inventory inventory) throws IOException {
|
||||
int capacity = inventory.capacity();
|
||||
out.writeShort(capacity);
|
||||
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item item = inventory.get(slot);
|
||||
if (item != null) {
|
||||
out.writeShort(item.getId() + 1);
|
||||
out.writeInt(item.getAmount());
|
||||
} else {
|
||||
out.writeShort(0);
|
||||
out.writeInt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+10
-12
@@ -1,35 +1,33 @@
|
||||
package org.apollo.io.player.impl;
|
||||
package org.apollo.io.player;
|
||||
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.game.model.entity.setting.MembershipStatus;
|
||||
import org.apollo.game.model.entity.setting.PrivilegeLevel;
|
||||
import org.apollo.io.player.PlayerLoader;
|
||||
import org.apollo.io.player.PlayerLoaderResponse;
|
||||
import org.apollo.net.codec.login.LoginConstants;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* A dummy {@link PlayerLoader} implementation used for testing purposes.
|
||||
* A {@link PlayerSerializer} that saves no data and returns an administrator member account, ideal for debugging.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public final class DummyPlayerLoader implements PlayerLoader {
|
||||
|
||||
/**
|
||||
* The default spawn position for players loaded by this loader.
|
||||
*/
|
||||
private static final Position DEFAULT_POSITION = new Position(3093, 3104);
|
||||
public final class DummyPlayerSerializer implements PlayerSerializer {
|
||||
|
||||
@Override
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) {
|
||||
int status = LoginConstants.STATUS_OK;
|
||||
|
||||
Player player = new Player(credentials, DEFAULT_POSITION);
|
||||
Player player = new Player(credentials, TUTORIAL_ISLAND_SPAWN);
|
||||
player.setPrivilegeLevel(PrivilegeLevel.ADMINISTRATOR);
|
||||
player.setMembers(MembershipStatus.PAID);
|
||||
|
||||
return new PlayerLoaderResponse(status, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) {
|
||||
/* discard player */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.apollo.io.player;
|
||||
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* A {@link PlayerSerializer} that utilises {@code JDBC} to communicate with an SQL database containing player data.
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class JdbcPlayerSerializer implements PlayerSerializer {
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) throws Exception {
|
||||
throw new UnsupportedOperationException("JDBC saving is not supported at this time.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception {
|
||||
throw new UnsupportedOperationException("JDBC loading is not supported at this time.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.apollo.io.player;
|
||||
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* An interface which may be extended by others which are capable of loading players. For example, implementations might
|
||||
* include text-based, binary and SQL loaders.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public interface PlayerLoader {
|
||||
|
||||
/**
|
||||
* Loads a player.
|
||||
*
|
||||
* @param credentials The player's credentials.
|
||||
* @return The {@link PlayerLoaderResponse}.
|
||||
* @throws Exception If an error occurs.
|
||||
*/
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception;
|
||||
|
||||
}
|
||||
@@ -8,9 +8,10 @@ import org.apollo.net.codec.login.LoginConstants;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* A response for the {@link PlayerLoader#loadPlayer(org.apollo.security.PlayerCredentials)} call.
|
||||
* A response for the {@link PlayerSerializer#loadPlayer} call.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public final class PlayerLoaderResponse {
|
||||
|
||||
@@ -32,7 +33,8 @@ public final class PlayerLoaderResponse {
|
||||
* {@link LoginConstants#STATUS_RECONNECTION_OK}.
|
||||
*/
|
||||
public PlayerLoaderResponse(int status) {
|
||||
Preconditions.checkArgument(status != LoginConstants.STATUS_OK && status != LoginConstants.STATUS_RECONNECTION_OK, "Player required for this status code.");
|
||||
Preconditions.checkArgument(status != LoginConstants.STATUS_OK && status != LoginConstants.STATUS_RECONNECTION_OK,
|
||||
"Player required for this status code.");
|
||||
this.status = status;
|
||||
player = Optional.empty();
|
||||
}
|
||||
@@ -46,7 +48,8 @@ public final class PlayerLoaderResponse {
|
||||
* @throws NullPointerException If the specified player is null.
|
||||
*/
|
||||
public PlayerLoaderResponse(int status, Player player) {
|
||||
Preconditions.checkArgument(status == LoginConstants.STATUS_OK || status == LoginConstants.STATUS_RECONNECTION_OK, "Player not required for this status code.");
|
||||
Preconditions.checkArgument(status == LoginConstants.STATUS_OK || status == LoginConstants.STATUS_RECONNECTION_OK,
|
||||
"Player not required for this status code.");
|
||||
this.status = status;
|
||||
this.player = Optional.of(player);
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.apollo.io.player;
|
||||
|
||||
import org.apollo.game.model.entity.Player;
|
||||
|
||||
/**
|
||||
* An interface which may be implemented by others which are capable of saving players. For example, implementations
|
||||
* might include text-based, binary and SQL savers.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public interface PlayerSaver {
|
||||
|
||||
/**
|
||||
* Saves a player.
|
||||
*
|
||||
* @param player The player to save.
|
||||
* @throws Exception If an error occurs.
|
||||
*/
|
||||
public void savePlayer(Player player) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.apollo.io.player;
|
||||
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* An interface which may be implemented by others which are capable of serializing and deserializing players. For
|
||||
* example, implementations might include text-based, binary and SQL serializers.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public interface PlayerSerializer {
|
||||
|
||||
/**
|
||||
* The spawn point for Players, on Tutorial Island.
|
||||
*/
|
||||
Position TUTORIAL_ISLAND_SPAWN = new Position(3093, 3104);
|
||||
|
||||
/**
|
||||
* Loads a {@link Player}.
|
||||
*
|
||||
* @param credentials The {@link PlayerCredentials}.
|
||||
* @return The {@link PlayerLoaderResponse}.
|
||||
* @throws Exception If an error occurs.
|
||||
*/
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception;
|
||||
|
||||
/**
|
||||
* Saves a {@link Player}.
|
||||
*
|
||||
* @param player The Player to save.
|
||||
* @throws Exception If an error occurs.
|
||||
*/
|
||||
public void savePlayer(Player player) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.apollo.io.player.impl;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apollo.game.model.Appearance;
|
||||
import org.apollo.game.model.Item;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.game.model.entity.Skill;
|
||||
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.inv.Inventory;
|
||||
import org.apollo.io.player.PlayerSaver;
|
||||
import org.apollo.util.NameUtil;
|
||||
import org.apollo.util.StreamUtil;
|
||||
|
||||
/**
|
||||
* A {@link PlayerSaver} implementation that saves player data to a binary file.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BinaryPlayerSaver implements PlayerSaver {
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) throws IOException {
|
||||
File file = BinaryPlayerUtil.getFile(player.getUsername());
|
||||
|
||||
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
|
||||
StreamUtil.writeString(out, player.getUsername());
|
||||
StreamUtil.writeString(out, player.getCredentials().getPassword());
|
||||
out.writeByte(player.getPrivilegeLevel().toInteger());
|
||||
out.writeByte(player.getMembershipStatus().getValue());
|
||||
|
||||
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());
|
||||
|
||||
Position position = player.getPosition();
|
||||
out.writeShort(position.getX());
|
||||
out.writeShort(position.getY());
|
||||
out.writeByte(position.getHeight());
|
||||
|
||||
Appearance appearance = player.getAppearance();
|
||||
out.writeByte(appearance.getGender().toInteger());
|
||||
int[] style = appearance.getStyle();
|
||||
for (int element : style) {
|
||||
out.writeByte(element);
|
||||
}
|
||||
int[] colors = appearance.getColors();
|
||||
for (int color : colors) {
|
||||
out.writeByte(color);
|
||||
}
|
||||
|
||||
writeInventory(out, player.getInventory());
|
||||
writeInventory(out, player.getEquipment());
|
||||
writeInventory(out, player.getBank());
|
||||
|
||||
SkillSet skills = player.getSkillSet();
|
||||
out.writeByte(skills.size());
|
||||
for (int id = 0; id < skills.size(); id++) {
|
||||
Skill skill = skills.getSkill(id);
|
||||
out.writeByte(skill.getCurrentLevel());
|
||||
out.writeDouble(skill.getExperience());
|
||||
}
|
||||
|
||||
List<String> usernames = player.getFriendUsernames();
|
||||
out.writeByte(usernames.size());
|
||||
for (String username : usernames) {
|
||||
out.writeLong(NameUtil.encodeBase37(username));
|
||||
}
|
||||
|
||||
usernames = player.getIgnoredUsernames();
|
||||
out.writeByte(usernames.size());
|
||||
for (String username : usernames) {
|
||||
out.writeLong(NameUtil.encodeBase37(username));
|
||||
}
|
||||
|
||||
Set<Entry<String, Attribute<?>>> attributes = player.getAttributes().entrySet();
|
||||
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);
|
||||
|
||||
Attribute<?> attribute = entry.getValue();
|
||||
out.writeByte(attribute.getType().getValue());
|
||||
out.write(attribute.encode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an inventory to the specified output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param inventory The inventory.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private static void writeInventory(DataOutputStream out, Inventory inventory) throws IOException {
|
||||
int capacity = inventory.capacity();
|
||||
out.writeShort(capacity);
|
||||
|
||||
for (int slot = 0; slot < capacity; slot++) {
|
||||
Item item = inventory.get(slot);
|
||||
if (item != null) {
|
||||
out.writeShort(item.getId() + 1);
|
||||
out.writeInt(item.getAmount());
|
||||
} else {
|
||||
out.writeShort(0);
|
||||
out.writeInt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.apollo.io.player.impl;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apollo.util.NameUtil;
|
||||
|
||||
/**
|
||||
* A utility class with common functionality used by the binary player loader/ savers.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BinaryPlayerUtil {
|
||||
|
||||
/**
|
||||
* The saved games directory.
|
||||
*/
|
||||
private static final File SAVED_GAMES_DIRECTORY = new File("data/savedGames");
|
||||
|
||||
/**
|
||||
* Creates the saved games directory if it does not exist.
|
||||
*/
|
||||
static {
|
||||
if (!SAVED_GAMES_DIRECTORY.exists()) {
|
||||
SAVED_GAMES_DIRECTORY.mkdir();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the save {@link File} for the specified player.
|
||||
*
|
||||
* @param username The username of the player.
|
||||
* @return The file.
|
||||
*/
|
||||
public static File getFile(String username) {
|
||||
String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
|
||||
return new File(SAVED_GAMES_DIRECTORY, filtered + ".dat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation.
|
||||
*/
|
||||
private BinaryPlayerUtil() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.apollo.io.player.impl;
|
||||
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.io.player.PlayerSaver;
|
||||
|
||||
/**
|
||||
* A {@link PlayerSaver} implementation that discards player data.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public final class DiscardPlayerSaver implements PlayerSaver {
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) {
|
||||
/* discard player */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.apollo.io.player.impl;
|
||||
|
||||
import org.apollo.io.player.PlayerLoader;
|
||||
import org.apollo.io.player.PlayerLoaderResponse;
|
||||
import org.apollo.security.PlayerCredentials;
|
||||
|
||||
/**
|
||||
* A {@link PlayerLoader} that utilises {@code JDBC} to load player files.
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class JdbcPlayerLoader implements PlayerLoader {
|
||||
|
||||
@Override
|
||||
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception {
|
||||
throw new UnsupportedOperationException("JDBC loading is not supported at this time.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.apollo.io.player.impl;
|
||||
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.io.player.PlayerSaver;
|
||||
|
||||
/**
|
||||
* A {@link PlayerSaver} that utilises {@code JDBC} to save the player.
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class JdbcPlayerSaver implements PlayerSaver {
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) throws Exception {
|
||||
throw new UnsupportedOperationException("JDBC saving is not supported at this time.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains various player loader/saver implementations.
|
||||
*/
|
||||
package org.apollo.io.player.impl;
|
||||
@@ -8,9 +8,8 @@ import java.util.concurrent.Executors;
|
||||
|
||||
import org.apollo.Service;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.io.player.PlayerLoader;
|
||||
import org.apollo.io.player.PlayerLoaderResponse;
|
||||
import org.apollo.io.player.PlayerSaver;
|
||||
import org.apollo.io.player.PlayerSerializer;
|
||||
import org.apollo.net.codec.login.LoginConstants;
|
||||
import org.apollo.net.codec.login.LoginRequest;
|
||||
import org.apollo.net.release.Release;
|
||||
@@ -25,6 +24,7 @@ import org.xml.sax.SAXException;
|
||||
* The {@link LoginService} manages {@link LoginRequest}s.
|
||||
*
|
||||
* @author Graham
|
||||
* @author Major
|
||||
*/
|
||||
public final class LoginService extends Service {
|
||||
|
||||
@@ -34,14 +34,9 @@ public final class LoginService extends Service {
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("LoginService"));
|
||||
|
||||
/**
|
||||
* The current {@link PlayerLoader}.
|
||||
* The current {@link PlayerSerializer}.
|
||||
*/
|
||||
private PlayerLoader loader;
|
||||
|
||||
/**
|
||||
* The current {@link PlayerSaver}.
|
||||
*/
|
||||
private PlayerSaver saver;
|
||||
private PlayerSerializer serializer;
|
||||
|
||||
/**
|
||||
* Creates the login service.
|
||||
@@ -70,24 +65,16 @@ public final class LoginService extends Service {
|
||||
}
|
||||
|
||||
if (!rootNode.getName().equals("login")) {
|
||||
throw new IOException("Unexpected root node name.");
|
||||
throw new IOException("Unexpected root node name, expected 'login'.");
|
||||
}
|
||||
|
||||
XmlNode loaderNode = rootNode.getChild("loader");
|
||||
if (loaderNode == null || !loaderNode.hasValue()) {
|
||||
throw new IOException("No loader child node or value.");
|
||||
XmlNode serializer = rootNode.getChild("serializer");
|
||||
if (serializer == null || !serializer.hasValue()) {
|
||||
throw new IOException("No serializer child node or value.");
|
||||
}
|
||||
|
||||
XmlNode saverNode = rootNode.getChild("saver");
|
||||
if (saverNode == null || !saverNode.hasValue()) {
|
||||
throw new IOException("No saver child node or value.");
|
||||
}
|
||||
|
||||
Class<?> loaderClazz = Class.forName(loaderNode.getValue());
|
||||
Class<?> saverClazz = Class.forName(saverNode.getValue());
|
||||
|
||||
loader = (PlayerLoader) loaderClazz.newInstance();
|
||||
saver = (PlayerSaver) saverClazz.newInstance();
|
||||
Class<?> clazz = Class.forName(serializer.getValue());
|
||||
this.serializer = (PlayerSerializer) clazz.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +82,7 @@ public final class LoginService extends Service {
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
/* empty - here for consistency with other services */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +97,7 @@ public final class LoginService extends Service {
|
||||
// TODO check archive 0 CRCs
|
||||
session.handlePlayerLoaderResponse(request, new PlayerLoaderResponse(LoginConstants.STATUS_GAME_UPDATED));
|
||||
} else {
|
||||
executor.submit(new PlayerLoaderWorker(loader, session, request));
|
||||
executor.submit(new PlayerLoaderWorker(serializer, session, request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +108,7 @@ public final class LoginService extends Service {
|
||||
* @param player The player to save.
|
||||
*/
|
||||
public void submitSaveRequest(GameSession session, Player player) {
|
||||
executor.submit(new PlayerSaverWorker(saver, session, player));
|
||||
executor.submit(new PlayerSaverWorker(serializer, session, player));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package org.apollo.login;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.io.player.PlayerLoader;
|
||||
import org.apollo.io.player.PlayerLoaderResponse;
|
||||
import org.apollo.io.player.PlayerSerializer;
|
||||
import org.apollo.net.codec.login.LoginConstants;
|
||||
import org.apollo.net.codec.login.LoginRequest;
|
||||
import org.apollo.net.session.LoginSession;
|
||||
@@ -22,9 +22,9 @@ public final class PlayerLoaderWorker implements Runnable {
|
||||
private static final Logger logger = Logger.getLogger(PlayerLoaderWorker.class.getName());
|
||||
|
||||
/**
|
||||
* The player loader.
|
||||
* The PlayerSerializer.
|
||||
*/
|
||||
private final PlayerLoader loader;
|
||||
private final PlayerSerializer loader;
|
||||
|
||||
/**
|
||||
* The request.
|
||||
@@ -39,11 +39,11 @@ public final class PlayerLoaderWorker implements Runnable {
|
||||
/**
|
||||
* Creates a {@link PlayerLoaderWorker} which will do the work for a single player load request.
|
||||
*
|
||||
* @param loader The current player loader.
|
||||
* @param loader The {@link PlayerSerializer}.
|
||||
* @param session The {@link LoginSession} which initiated the request.
|
||||
* @param request The {@link LoginRequest} object.
|
||||
*/
|
||||
public PlayerLoaderWorker(PlayerLoader loader, LoginSession session, LoginRequest request) {
|
||||
public PlayerLoaderWorker(PlayerSerializer loader, LoginSession session, LoginRequest request) {
|
||||
this.loader = loader;
|
||||
this.session = session;
|
||||
this.request = request;
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.io.player.PlayerSaver;
|
||||
import org.apollo.io.player.PlayerSerializer;
|
||||
import org.apollo.net.session.GameSession;
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ public final class PlayerSaverWorker implements Runnable {
|
||||
/**
|
||||
* The player saver.
|
||||
*/
|
||||
private final PlayerSaver saver;
|
||||
private final PlayerSerializer saver;
|
||||
|
||||
/**
|
||||
* The game session.
|
||||
@@ -41,7 +41,7 @@ public final class PlayerSaverWorker implements Runnable {
|
||||
* @param session The game session.
|
||||
* @param player The player to save.
|
||||
*/
|
||||
public PlayerSaverWorker(PlayerSaver saver, GameSession session, Player player) {
|
||||
public PlayerSaverWorker(PlayerSerializer saver, GameSession session, Player player) {
|
||||
this.saver = saver;
|
||||
this.session = session;
|
||||
this.player = player;
|
||||
|
||||
Reference in New Issue
Block a user