diff --git a/gameserver/build.gradle b/gameserver/build.gradle index ed3964f..7f0d017 100644 --- a/gameserver/build.gradle +++ b/gameserver/build.gradle @@ -10,8 +10,8 @@ repositories { dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.15.0' implementation 'org.apache.logging.log4j:log4j-core:2.15.0' - implementation 'org.msgpack:msgpack:0.6.12' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.4' + implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.0' implementation 'org.yaml:snakeyaml:1.27' implementation 'org.reflections:reflections:0.9.12' implementation 'io.netty:netty-all:4.1.58.Final' diff --git a/gameserver/src/main/java/brainwine/gameserver/GameServer.java b/gameserver/src/main/java/brainwine/gameserver/GameServer.java index f0fd7f4..8ecc27a 100644 --- a/gameserver/src/main/java/brainwine/gameserver/GameServer.java +++ b/gameserver/src/main/java/brainwine/gameserver/GameServer.java @@ -9,7 +9,6 @@ import org.apache.logging.log4j.Logger; import brainwine.gameserver.command.CommandManager; import brainwine.gameserver.entity.player.PlayerManager; import brainwine.gameserver.loot.LootManager; -import brainwine.gameserver.msgpack.MessagePackHelper; import brainwine.gameserver.prefab.PrefabManager; import brainwine.gameserver.server.NetworkRegistry; import brainwine.gameserver.server.Server; @@ -38,7 +37,6 @@ public class GameServer { logger.info("Starting GameServer ..."); CommandManager.init(); GameConfiguration.init(); - MessagePackHelper.init(); lootManager = new LootManager(); prefabManager = new PrefabManager(); StaticZoneGenerator.init(); diff --git a/gameserver/src/main/java/brainwine/gameserver/command/commands/ExportCommand.java b/gameserver/src/main/java/brainwine/gameserver/command/commands/ExportCommand.java index 457e06a..9fccc54 100644 --- a/gameserver/src/main/java/brainwine/gameserver/command/commands/ExportCommand.java +++ b/gameserver/src/main/java/brainwine/gameserver/command/commands/ExportCommand.java @@ -68,7 +68,7 @@ public class ExportCommand extends Command { executor.notify("Exporting your prefab ...", ALERT); try { - prefabManager.registerPrefab(name, prefab); + prefabManager.addPrefab(name, prefab); executor.notify(String.format("Your prefab '%s' was successfully exported!", name), ALERT); } catch (Exception e) { executor.notify(String.format("An error occured while exporting prefab '%s': %s", name, e.getMessage()), ALERT); diff --git a/gameserver/src/main/java/brainwine/gameserver/dialog/ConfigurableDialog.java b/gameserver/src/main/java/brainwine/gameserver/dialog/ConfigurableDialog.java index d4438d2..82e2d98 100644 --- a/gameserver/src/main/java/brainwine/gameserver/dialog/ConfigurableDialog.java +++ b/gameserver/src/main/java/brainwine/gameserver/dialog/ConfigurableDialog.java @@ -13,7 +13,7 @@ public abstract class ConfigurableDialog implements DialogComponent { private final Map config = new HashMap<>(); public abstract void init(); - public abstract void handleResponse(Player player, String[] input); + public abstract void handleResponse(Player player, Object[] input); protected void addSection(DialogSection section) { List> sections = (List>)config.getOrDefault("sections", new ArrayList<>()); diff --git a/gameserver/src/main/java/brainwine/gameserver/dialog/dialogs/RegistrationDialog.java b/gameserver/src/main/java/brainwine/gameserver/dialog/dialogs/RegistrationDialog.java index 8e35f15..2837c44 100644 --- a/gameserver/src/main/java/brainwine/gameserver/dialog/dialogs/RegistrationDialog.java +++ b/gameserver/src/main/java/brainwine/gameserver/dialog/dialogs/RegistrationDialog.java @@ -37,14 +37,15 @@ public class RegistrationDialog extends ConfigurableDialog { } @Override - public void handleResponse(Player player, String[] input) { + public void handleResponse(Player player, Object[] input) { if(input.length != 2) { player.alert("Incorrect number of parameters."); return; } - String email = input[0]; - String password = input[1]; + // TODO toString() for now, dialog system will be worked anyway. + String email = input[0].toString(); + String password = input[1].toString(); if(email.length() > maxEmailLength || !emailPattern.matcher(email).matches()) { player.alert("Please enter a valid e-mail address."); diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/EntityStatus.java b/gameserver/src/main/java/brainwine/gameserver/entity/EntityStatus.java index b16c6e0..78abe1d 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/EntityStatus.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/EntityStatus.java @@ -1,8 +1,5 @@ package brainwine.gameserver.entity; -import brainwine.gameserver.msgpack.RegisterEnum; - -@RegisterEnum public enum EntityStatus { EXITING, diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/EntityType.java b/gameserver/src/main/java/brainwine/gameserver/entity/EntityType.java index 7053b92..fd3d359 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/EntityType.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/EntityType.java @@ -1,9 +1,7 @@ package brainwine.gameserver.entity; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum EntityType { PLAYER(0), @@ -17,7 +15,7 @@ public enum EntityType { this.id = id; } - @EnumValue + @JsonValue public int getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/FacingDirection.java b/gameserver/src/main/java/brainwine/gameserver/entity/FacingDirection.java index 1620085..b16f2ae 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/FacingDirection.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/FacingDirection.java @@ -1,13 +1,11 @@ package brainwine.gameserver.entity; -import brainwine.gameserver.msgpack.DefaultEnumValue; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum FacingDirection { - @DefaultEnumValue + @JsonEnumDefaultValue WEST(-1), EAST(1); @@ -17,7 +15,7 @@ public enum FacingDirection { this.id = id; } - @EnumValue + @JsonValue public int getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/ChatType.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/ChatType.java index b0531aa..d12f0dd 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/ChatType.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/ChatType.java @@ -1,9 +1,7 @@ package brainwine.gameserver.entity.player; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum ChatType { CHAT("c"), @@ -17,7 +15,7 @@ public enum ChatType { this.id = id; } - @EnumValue + @JsonValue public String getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/ContainerType.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/ContainerType.java index c7d9027..99ab940 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/ContainerType.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/ContainerType.java @@ -1,9 +1,7 @@ package brainwine.gameserver.entity.player; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum ContainerType { INVENTORY("i"), @@ -16,7 +14,7 @@ public enum ContainerType { this.id = id; } - @EnumValue + @JsonValue public String getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/KarmaLevel.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/KarmaLevel.java index 5ccb197..06a3e0e 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/KarmaLevel.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/KarmaLevel.java @@ -3,10 +3,6 @@ package brainwine.gameserver.entity.player; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import com.fasterxml.jackson.annotation.JsonValue; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; - -@RegisterEnum public enum KarmaLevel { GODLY("Godly", 500), @@ -28,7 +24,6 @@ public enum KarmaLevel { } @JsonValue - @EnumValue public String getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/NotificationType.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/NotificationType.java index fd24879..0980cf1 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/NotificationType.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/NotificationType.java @@ -1,9 +1,7 @@ package brainwine.gameserver.entity.player; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum NotificationType { ALERT(1), @@ -25,7 +23,7 @@ public enum NotificationType { this.id = id; } - @EnumValue + @JsonValue public int getId() { return id; } diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java index 93253a2..d457793 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java @@ -318,7 +318,7 @@ public class Player extends Entity implements CommandExecutor { sendMessage(new DialogMessage(id, dialog)); } - public void handleDialogInput(int id, String[] input) { + public void handleDialogInput(int id, Object[] input) { ConfigurableDialog dialog = dialogs.remove(id); if(dialog == null) { diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/Skill.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/Skill.java index 085fd8c..9748673 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/Skill.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/Skill.java @@ -1,9 +1,7 @@ package brainwine.gameserver.entity.player; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; +import com.fasterxml.jackson.annotation.JsonValue; -@RegisterEnum public enum Skill { AGILITY, @@ -19,7 +17,7 @@ public enum Skill { STAMINA, SURVIVAL; - @EnumValue + @JsonValue public String getId() { return toString().toLowerCase(); } diff --git a/gameserver/src/main/java/brainwine/gameserver/item/Layer.java b/gameserver/src/main/java/brainwine/gameserver/item/Layer.java index 2656579..28c6cde 100644 --- a/gameserver/src/main/java/brainwine/gameserver/item/Layer.java +++ b/gameserver/src/main/java/brainwine/gameserver/item/Layer.java @@ -2,10 +2,6 @@ package brainwine.gameserver.item; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; -import brainwine.gameserver.msgpack.DefaultEnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; - -@RegisterEnum public enum Layer { BASE, @@ -13,7 +9,6 @@ public enum Layer { FRONT, LIQUID, - @DefaultEnumValue @JsonEnumDefaultValue NONE; } \ No newline at end of file diff --git a/gameserver/src/main/java/brainwine/gameserver/item/LootGraphic.java b/gameserver/src/main/java/brainwine/gameserver/item/LootGraphic.java index 39ca18a..904cb5c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/item/LootGraphic.java +++ b/gameserver/src/main/java/brainwine/gameserver/item/LootGraphic.java @@ -1,11 +1,8 @@ package brainwine.gameserver.item; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import com.fasterxml.jackson.annotation.JsonValue; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; - -@RegisterEnum public enum LootGraphic { LOOT, @@ -15,7 +12,7 @@ public enum LootGraphic { @JsonEnumDefaultValue NONE; - @EnumValue + @JsonValue public String getId() { return toString().toLowerCase(); } diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/DefaultEnumValue.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/DefaultEnumValue.java deleted file mode 100644 index 8d4d35e..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/DefaultEnumValue.java +++ /dev/null @@ -1,10 +0,0 @@ -package brainwine.gameserver.msgpack; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.FIELD, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface DefaultEnumValue {} \ No newline at end of file diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/EnumValue.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/EnumValue.java deleted file mode 100644 index 716609c..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/EnumValue.java +++ /dev/null @@ -1,14 +0,0 @@ -package brainwine.gameserver.msgpack; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Can be assigned to a field in an Enumeration. - * When packed, the value of the first field with this annotation will be used instead of the ordinal. - */ -@Target({ElementType.FIELD, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface EnumValue {} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/MessagePackHelper.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/MessagePackHelper.java deleted file mode 100644 index 3f06482..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/MessagePackHelper.java +++ /dev/null @@ -1,107 +0,0 @@ -package brainwine.gameserver.msgpack; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.zip.DataFormatException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.msgpack.MessagePack; -import org.msgpack.packer.BufferPacker; -import org.msgpack.unpacker.BufferUnpacker; - -import brainwine.gameserver.item.Item; -import brainwine.gameserver.msgpack.models.AppearanceData; -import brainwine.gameserver.msgpack.models.BlockUseData; -import brainwine.gameserver.msgpack.models.DialogInputData; -import brainwine.gameserver.msgpack.templates.AppearanceDataTemplate; -import brainwine.gameserver.msgpack.templates.BlockArrayTemplate; -import brainwine.gameserver.msgpack.templates.BlockTemplate; -import brainwine.gameserver.msgpack.templates.BlockUseDataTemplate; -import brainwine.gameserver.msgpack.templates.ChunkTemplate; -import brainwine.gameserver.msgpack.templates.DialogInputDataTemplate; -import brainwine.gameserver.msgpack.templates.EnumTemplate; -import brainwine.gameserver.msgpack.templates.ItemTemplate; -import brainwine.gameserver.msgpack.templates.PrefabTemplate; -import brainwine.gameserver.prefab.Prefab; -import brainwine.gameserver.util.ReflectionsHelper; -import brainwine.gameserver.util.ZipUtils; -import brainwine.gameserver.zone.Block; -import brainwine.gameserver.zone.Chunk; - -/** - * Static instance for the MsgPack library. - */ -public class MessagePackHelper { - - private static final Logger logger = LogManager.getLogger(); - private static final MessagePack messagePack = new MessagePack(); - - public static void init() { - registerTemplates(); - } - - private static void registerTemplates() { - logger.info("Registering MessagePack templates ..."); - messagePack.register(Item.class, new ItemTemplate()); - messagePack.register(Block.class, new BlockTemplate()); - messagePack.register(Block[].class, new BlockArrayTemplate()); - messagePack.register(Chunk.class, new ChunkTemplate()); - messagePack.register(Prefab.class, new PrefabTemplate()); - messagePack.register(BlockUseData.class, new BlockUseDataTemplate()); - messagePack.register(DialogInputData.class, new DialogInputDataTemplate()); - messagePack.register(AppearanceData.class, new AppearanceDataTemplate()); - registerEnumTemplates(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static void registerEnumTemplates() { - for(Class clazz : ReflectionsHelper.getTypesAnnotatedWith(RegisterEnum.class)) { - messagePack.register(clazz, new EnumTemplate(clazz)); - } - } - - public static BufferUnpacker readFile(File file) throws IOException, DataFormatException { - byte[] bytes = Files.readAllBytes(file.toPath()); - bytes = ZipUtils.inflateBytes(bytes); - return createBufferUnpacker(bytes); - } - - public static BufferUnpacker readFiles(File... files) throws IOException, DataFormatException, IndexOutOfBoundsException { - byte[] buffer = new byte[Short.MAX_VALUE]; - int index = 0; - - for(File file : files) { - byte[] bytes = Files.readAllBytes(file.toPath()); - bytes = ZipUtils.inflateBytes(bytes); - System.arraycopy(bytes, 0, buffer, index, bytes.length); - index += bytes.length; - } - - byte[] bytes = new byte[index]; - System.arraycopy(buffer, 0, bytes, 0, bytes.length); - return createBufferUnpacker(bytes); - } - - public static void writeToFile(File file, Object... objects) throws IOException { - BufferPacker packer = createBufferPacker(); - - for(Object object : objects) { - packer.write(object); - } - - byte[] bytes = packer.toByteArray(); - bytes = ZipUtils.deflateBytes(bytes); - packer.close(); - Files.write(file.toPath(), bytes); - } - - public static BufferUnpacker createBufferUnpacker(byte[] bytes) { - return messagePack.createBufferUnpacker(bytes); - } - - public static BufferPacker createBufferPacker() { - return messagePack.createBufferPacker(); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/RegisterEnum.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/RegisterEnum.java deleted file mode 100644 index abd1d29..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/RegisterEnum.java +++ /dev/null @@ -1,13 +0,0 @@ -package brainwine.gameserver.msgpack; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Enumerations with this annotation will automatically be registered with {@link EnumTemplate} - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RegisterEnum {} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/AppearanceData.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/models/AppearanceData.java deleted file mode 100644 index 2522bad..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/AppearanceData.java +++ /dev/null @@ -1,15 +0,0 @@ -package brainwine.gameserver.msgpack.models; - -import java.util.HashMap; - -/** - * Extremely lazy, yes... - * Lazy, but it does the trick. - */ -public class AppearanceData extends HashMap { - - /** - * - */ - private static final long serialVersionUID = -688912102884421443L; -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/BlockUseData.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/models/BlockUseData.java deleted file mode 100644 index fac824f..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/BlockUseData.java +++ /dev/null @@ -1,17 +0,0 @@ -package brainwine.gameserver.msgpack.models; - -/** - * Simple wrapper to avoid having to force our custom template on object arrays. - */ -public class BlockUseData { - - private final Object[] data; - - public BlockUseData(Object[] data) { - this.data = data; - } - - public Object[] getData() { - return data; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/DialogInputData.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/models/DialogInputData.java deleted file mode 100644 index d865a93..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/models/DialogInputData.java +++ /dev/null @@ -1,57 +0,0 @@ -package brainwine.gameserver.msgpack.models; - -import brainwine.gameserver.server.requests.DialogRequest; - -/** - * For {@link DialogRequest} - * TODO Figure out more about all this. - */ -public class DialogInputData { - - private String dialogName; - private int dialogId; - private String[] inputData; - private String action; - - public DialogInputData(String dialogName) { - this.dialogName = dialogName; - } - - public DialogInputData(int dialogId, String[] inputData) { - this.dialogId = dialogId; - this.inputData = inputData; - } - - public DialogInputData(int dialogId, String action) { - this.dialogId = dialogId; - this.action = action; - } - - public boolean isType1() { - return dialogName != null; - } - - public boolean isType2() { - return dialogId != 0 && inputData != null; - } - - public boolean isType3() { - return dialogId != 0 && action != null; - } - - public String getDialogName() { - return dialogName; - } - - public int getDialogId() { - return dialogId; - } - - public String[] getInputData() { - return inputData; - } - - public String getAction() { - return action; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/AppearanceDataTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/AppearanceDataTemplate.java deleted file mode 100644 index 44e83bc..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/AppearanceDataTemplate.java +++ /dev/null @@ -1,60 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.msgpack.models.AppearanceData; - -public class AppearanceDataTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, AppearanceData data, boolean required) throws IOException { - if(data == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.write(data); - } - - @Override - public AppearanceData read(Unpacker unpacker, AppearanceData to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - if(unpacker.getNextType() != ValueType.MAP) { - throw new MessageTypeException("Invalid data type"); - } - - AppearanceData data = new AppearanceData(); - int numEntries = unpacker.readMapBegin(); - - for(int i = 0; i < numEntries; i++) { - String key = unpacker.readString(); - Object value = null; - - if(unpacker.getNextType() == ValueType.RAW) { - value = unpacker.readString(); - } else if(unpacker.getNextType() == ValueType.INTEGER) { - value = unpacker.readInt(); - } else { - throw new MessageTypeException("Invalid data type"); - } - - data.put(key, value); - } - - unpacker.readMapEnd(); - return data; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockArrayTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockArrayTemplate.java deleted file mode 100644 index f8047c1..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockArrayTemplate.java +++ /dev/null @@ -1,49 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.zone.Block; - -public class BlockArrayTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, Block[] blocks, boolean required) throws IOException { - if(blocks == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.writeArrayBegin(blocks.length * 3); - - for(Block block : blocks) { - packer.write(block); - } - - packer.writeArrayEnd(); - } - - @Override - public Block[] read(Unpacker unpacker, Block[] to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - Block[] blocks = new Block[unpacker.readArrayBegin() / 3]; - - for(int i = 0; i < blocks.length; i++) { - blocks[i] = unpacker.read(Block.class); - } - - unpacker.readArrayEnd(); - return blocks; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockTemplate.java deleted file mode 100644 index 7276297..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockTemplate.java +++ /dev/null @@ -1,41 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.zone.Block; - -public class BlockTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, Block block, boolean required) throws IOException { - if(block == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.write(block.getBaseItem().getId() | (((block.getLiquidItem().getId() & 255) << 8) | ((block.getLiquidMod() & 31) << 16))); - packer.write(block.getBackItem().getId() | ((block.getBackMod() & 31) << 16)); - packer.write(block.getFrontItem().getId() | ((block.getFrontMod() & 31) << 16)); - } - - @Override - public Block read(Unpacker unpacker, Block to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - int base = unpacker.readInt(); - int back = unpacker.readInt(); - int front = unpacker.readInt(); - return new Block(base & 15, back & 65535, back >> 16 & 31, front & 65535, front >> 16 & 31, base >> 8 & 255, base >> 16 & 31); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockUseDataTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockUseDataTemplate.java deleted file mode 100644 index f83b157..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/BlockUseDataTemplate.java +++ /dev/null @@ -1,71 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.msgpack.models.BlockUseData; - -public class BlockUseDataTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, BlockUseData data, boolean required) throws IOException { - if(data == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.write(data.getData()); - } - - @Override - public BlockUseData read(Unpacker unpacker, BlockUseData to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - if(unpacker.getNextType() == ValueType.ARRAY) { - Object[] data = new Object[unpacker.readArrayBegin()]; - - for(int i = 0; i < data.length; i++) { - data[i] = readObject(unpacker); - } - - unpacker.readArrayEnd(); - return new BlockUseData(data); - } else if(unpacker.getNextType() == ValueType.MAP) { - Object[] data = new Object[unpacker.readMapBegin()]; - - for(int i = 0; i < data.length; i++) { - unpacker.readString(); // Key, ignore - data[i] = readObject(unpacker); - } - - unpacker.readMapEnd(); - return new BlockUseData(data); - } - - throw new MessageTypeException("Invalid data type"); - } - - private Object readObject(Unpacker unpacker) throws IOException { - switch(unpacker.getNextType()) { - case RAW: - return unpacker.readString(); - case INTEGER: - return unpacker.readInt(); - case FLOAT: - return unpacker.readFloat(); - default: - throw new MessageTypeException("Invalid data type"); - } - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ChunkTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ChunkTemplate.java deleted file mode 100644 index 344f9c9..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ChunkTemplate.java +++ /dev/null @@ -1,57 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.zone.Block; -import brainwine.gameserver.zone.Chunk; - -public class ChunkTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, Chunk chunk, boolean required) throws IOException { - if(chunk == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - Block[] blocks = chunk.getBlocks(); - packer.writeArrayBegin(5); - packer.write(chunk.getX()); - packer.write(chunk.getY()); - packer.write(chunk.getWidth()); - packer.write(chunk.getHeight()); - packer.write(blocks); - packer.writeArrayEnd(); - } - - @Override - public Chunk read(Unpacker unpacker, Chunk to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - unpacker.readArrayBegin(); - int x = unpacker.readInt(); - int y = unpacker.readInt(); - int width = unpacker.readInt(); - int height = unpacker.readInt(); - Block[] blocks = unpacker.read(Block[].class); - Chunk chunk = new Chunk(x, y, width, height); - - for(int i = 0; i < blocks.length; i++) { - chunk.setBlock(i, blocks[i]); - } - - unpacker.readArrayEnd(); - return chunk; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/DialogInputDataTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/DialogInputDataTemplate.java deleted file mode 100644 index c404ea3..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/DialogInputDataTemplate.java +++ /dev/null @@ -1,67 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.msgpack.models.DialogInputData; - -public class DialogInputDataTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, DialogInputData data, boolean required) throws IOException { - if(data == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - if(data.isType1()) { - packer.write(data.getDialogName()); - } else if(data.isType2()) { - packer.write(data.getDialogId()); - packer.write(data.getInputData()); - } - } - - @Override - public DialogInputData read(Unpacker unpacker, DialogInputData to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - if(unpacker.getNextType() == ValueType.RAW) { - String dialogName = unpacker.readString(); - unpacker.readValue(); // TODO find out if this is the action, or just garbage data. - return new DialogInputData(dialogName); - } else if(unpacker.getNextType() == ValueType.INTEGER) { - int id = unpacker.readInt(); - - if(unpacker.getNextType() == ValueType.RAW) { - return new DialogInputData(id, unpacker.readString()); - } else if(unpacker.getNextType() == ValueType.ARRAY) { - return new DialogInputData(id, unpacker.read(String[].class)); - } - - int numEntries = unpacker.readMapBegin(); - String[] input = new String[numEntries]; - - for(int i = 0; i < numEntries; i++) { - unpacker.readString(); // Key, ignore - input[i] = unpacker.readString(); - } - - unpacker.readMapEnd(); - return new DialogInputData(id, input); - } - - throw new MessageTypeException("Invalid data type"); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/EnumTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/EnumTemplate.java deleted file mode 100644 index fd0fc9c..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/EnumTemplate.java +++ /dev/null @@ -1,106 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.msgpack.DefaultEnumValue; -import brainwine.gameserver.msgpack.EnumValue; - -@SuppressWarnings("unchecked") -public class EnumTemplate extends AbstractTemplate { - - private final Map ids = new HashMap<>(); - private final Map values = new HashMap<>(); - private T defaultValue; - - public EnumTemplate(Class type) { - T[] entries = type.getEnumConstants(); - - for(Field field : type.getFields()) { - if(field.getType() == type && field.isAnnotationPresent(DefaultEnumValue.class)) { - try { - defaultValue = (T)field.get(type); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new MessageTypeException(e); - } - } - - if(field.isAnnotationPresent(EnumValue.class)) { - try { - for(T entry : entries) { - Object id = field.get(entry); - ids.put(entry, id); - values.put(id, entry); - } - - return; - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new MessageTypeException(e); - } - } - } - - for(Method method : type.getMethods()) { - if(method.isAnnotationPresent(EnumValue.class)) { - try { - for(T entry : entries) { - Object id = method.invoke(entry); - ids.put(entry, id); - values.put(id, entry); - } - - return; - } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { - throw new MessageTypeException(e); - } - } - } - - for(int i = 0; i < entries.length; i++) { - ids.put(entries[i], i); - values.put(i, entries[i]); - } - } - - @Override - public void write(Packer packer, T target, boolean required) throws IOException { - if(target == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - // Dangerous, might throw an NPE - packer.write(ids.get(target)); - } - - @Override - public T read(Unpacker unpacker, T to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - ValueType next = unpacker.getNextType(); - - if(next == ValueType.INTEGER) { - return values.getOrDefault(unpacker.readInt(), defaultValue); - } else if(next == ValueType.RAW) { - return values.getOrDefault(unpacker.readString(), defaultValue); - } - - throw new MessageTypeException("Unsupported enum id type"); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ItemTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ItemTemplate.java deleted file mode 100644 index 764b0bb..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/ItemTemplate.java +++ /dev/null @@ -1,37 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.item.Item; -import brainwine.gameserver.item.ItemRegistry; - -public class ItemTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, Item item, boolean required) throws IOException { - if(item == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.write(item.getId()); - } - - @Override - public Item read(Unpacker unpacker, Item to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - return ItemRegistry.getItem(unpacker.readInt()); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/PrefabTemplate.java b/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/PrefabTemplate.java deleted file mode 100644 index 49b7742..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/msgpack/templates/PrefabTemplate.java +++ /dev/null @@ -1,50 +0,0 @@ -package brainwine.gameserver.msgpack.templates; - -import java.io.IOException; - -import org.msgpack.MessageTypeException; -import org.msgpack.packer.Packer; -import org.msgpack.template.AbstractTemplate; -import org.msgpack.unpacker.Unpacker; - -import brainwine.gameserver.prefab.Prefab; -import brainwine.gameserver.zone.Block; - -public class PrefabTemplate extends AbstractTemplate { - - @Override - public void write(Packer packer, Prefab prefab, boolean required) throws IOException { - if(prefab == null) { - if(required) { - throw new MessageTypeException("Attempted to write null"); - } - - packer.writeNil(); - return; - } - - packer.write(prefab.getWidth()); - packer.write(prefab.getHeight()); - packer.write(prefab.getBlocks()); - } - - @Override - public Prefab read(Unpacker unpacker, Prefab to, boolean required) throws IOException { - if(!required && unpacker.trySkipNil()) { - return null; - } - - int width = unpacker.readInt(); - int height = unpacker.readInt(); - Block[] blocks = unpacker.read(Block[].class); - - if(to != null) { - to.setWidth(width); - to.setHeight(height); - to.setBlocks(blocks); - return to; - } - - return new Prefab(width, height, blocks); - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/prefab/Prefab.java b/gameserver/src/main/java/brainwine/gameserver/prefab/Prefab.java index 9f94580..c03553a 100644 --- a/gameserver/src/main/java/brainwine/gameserver/prefab/Prefab.java +++ b/gameserver/src/main/java/brainwine/gameserver/prefab/Prefab.java @@ -4,60 +4,37 @@ import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import brainwine.gameserver.GameServer; import brainwine.gameserver.item.Item; import brainwine.gameserver.util.WeightedMap; import brainwine.gameserver.zone.Block; -@JsonIgnoreProperties(ignoreUnknown = true) public class Prefab { - @JsonProperty("dungeon") private boolean dungeon; - - @JsonProperty("ruin") private boolean ruin; - - @JsonProperty("loot") private boolean loot; - - @JsonProperty("decay") private boolean decay; - - @JsonProperty("mirrorable") private boolean mirrorable; - - @JsonProperty("replace") + private int width; + private int height; + private Block[] blocks; private Map> replacements = new HashMap<>(); - - @JsonProperty("corresponding_replace") private Map correspondingReplacements = new HashMap<>(); - - @JsonProperty("metadata") private Map> metadata = new HashMap<>(); - @JsonIgnore - private int width; - - @JsonIgnore - private int height; - - @JsonIgnore - private Block[] blocks; - - @JsonCreator - private Prefab() {} - - @JsonIgnore - public Prefab(int width, int height, Block[] blocks) { - this(width, height, blocks, new HashMap<>()); + protected Prefab(PrefabConfig config, PrefabBlockData blockData) { + this(blockData.getWidth(), blockData.getHeight(), blockData.getBlocks(), config.getMetadata()); + dungeon = config.isDungeon(); + ruin = config.isRuin(); + loot = config.hasLoot(); + decay = config.hasDecay(); + mirrorable = config.isMirrorable(); + replacements = config.getReplacements(); + correspondingReplacements = config.getCorrespondingReplacements(); } - @JsonIgnore public Prefab(int width, int height, Block[] blocks, Map> metadata) { this.width = width; this.height = height; @@ -89,6 +66,18 @@ public class Prefab { public boolean isMirrorable() { return mirrorable; } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Block[] getBlocks() { + return blocks; + } public Map getMetadata(int index) { return metadata.get(index); @@ -105,28 +94,4 @@ public class Prefab { public Map getCorrespondingReplacements() { return correspondingReplacements; } - - public void setWidth(int width) { - this.width = width; - } - - public int getWidth() { - return width; - } - - public void setHeight(int height) { - this.height = height; - } - - public int getHeight() { - return height; - } - - public void setBlocks(Block[] blocks) { - this.blocks = blocks; - } - - public Block[] getBlocks() { - return blocks; - } } diff --git a/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabBlockData.java b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabBlockData.java new file mode 100644 index 0000000..3c0f52f --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabBlockData.java @@ -0,0 +1,38 @@ +package brainwine.gameserver.prefab; + +import java.beans.ConstructorProperties; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import brainwine.gameserver.zone.Block; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class PrefabBlockData { + + private int width; + private int height; + private Block[] blocks; + + protected PrefabBlockData(Prefab prefab) { + this(prefab.getWidth(), prefab.getHeight(), prefab.getBlocks()); + } + + @ConstructorProperties({"width", "height", "blocks"}) + public PrefabBlockData(int width, int height, Block[] blocks) { + this.width = width; + this.height = height; + this.blocks = blocks; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Block[] getBlocks() { + return blocks; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabConfig.java b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabConfig.java new file mode 100644 index 0000000..27c1c90 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabConfig.java @@ -0,0 +1,85 @@ +package brainwine.gameserver.prefab; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import brainwine.gameserver.item.Item; +import brainwine.gameserver.util.WeightedMap; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class PrefabConfig { + + @JsonProperty("dungeon") + private boolean dungeon; + + @JsonProperty("ruin") + private boolean ruin; + + @JsonProperty("loot") + private boolean loot; + + @JsonProperty("decay") + private boolean decay; + + @JsonProperty("mirrorable") + private boolean mirrorable; + + @JsonProperty("replace") + private Map> replacements = new HashMap<>(); + + @JsonProperty("corresponding_replace") + private Map correspondingReplacements = new HashMap<>(); + + @JsonProperty("metadata") + private Map> metadata = new HashMap<>(); + + @JsonCreator + private PrefabConfig() {} + + protected PrefabConfig(Prefab prefab) { + dungeon = prefab.isDungeon(); + ruin = prefab.isRuin(); + loot = prefab.hasLoot(); + decay = prefab.hasDecay(); + mirrorable = prefab.isMirrorable(); + replacements = prefab.getReplacements(); + correspondingReplacements = prefab.getCorrespondingReplacements(); + metadata = prefab.getMetadata(); + } + + public boolean isDungeon() { + return dungeon; + } + + public boolean isRuin() { + return ruin; + } + + public boolean hasLoot() { + return loot; + } + + public boolean hasDecay() { + return decay; + } + + public boolean isMirrorable() { + return mirrorable; + } + + public Map> getMetadata() { + return metadata; + } + + public Map> getReplacements() { + return replacements; + } + + public Map getCorrespondingReplacements() { + return correspondingReplacements; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabManager.java b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabManager.java index 2d27da2..95a8d69 100644 --- a/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabManager.java +++ b/gameserver/src/main/java/brainwine/gameserver/prefab/PrefabManager.java @@ -9,24 +9,32 @@ import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.msgpack.unpacker.BufferUnpacker; +import org.msgpack.core.MessagePack; +import org.msgpack.core.MessageUnpacker; +import org.msgpack.jackson.dataformat.MessagePackFactory; import org.reflections.Reflections; import org.reflections.scanners.ResourcesScanner; -import brainwine.gameserver.msgpack.MessagePackHelper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import brainwine.gameserver.serialization.BlockDeserializer; +import brainwine.gameserver.serialization.BlockSerializer; +import brainwine.gameserver.util.ZipUtils; +import brainwine.gameserver.zone.Block; import brainwine.shared.JsonHelper; public class PrefabManager { private static final Logger logger = LogManager.getLogger(); + private static final ObjectMapper mapper = new ObjectMapper(new MessagePackFactory()) + .registerModule(new SimpleModule() + .addDeserializer(Block.class, BlockDeserializer.INSTANCE) + .addSerializer(BlockSerializer.INSTANCE)); private final File dataDir = new File("prefabs"); private final Map prefabs = new HashMap<>(); public PrefabManager() { - loadPrefabs(); - } - - private void loadPrefabs() { logger.info("Loading prefabs ..."); if(!dataDir.exists()) { @@ -60,34 +68,49 @@ public class PrefabManager { private void loadPrefab(File file) { String name = file.getName(); - File configFile = new File(file, "config.json"); + File legacyBlocksFile = new File(file, "blocks.cmp"); + File blocksFile = new File(file, "blocks.dat"); try { - Prefab prefab = JsonHelper.readValue(configFile, Prefab.class); - BufferUnpacker unpacker = MessagePackHelper.readFile(new File(file, "blocks.cmp")); - unpacker.read(prefab); - unpacker.close(); - prefabs.put(name, prefab); + PrefabBlockData blockData = null; + + if(legacyBlocksFile.exists() && !blocksFile.exists()) { + logger.info("Updating blocks file for prefab '{}' ...", name); + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker( + ZipUtils.inflateBytes(Files.readAllBytes(legacyBlocksFile.toPath()))); + int width = unpacker.unpackInt(); + int height = unpacker.unpackInt(); + Block[] blocks = new Block[unpacker.unpackArrayHeader() / 3]; + + for(int i = 0; i < blocks.length; i++) { + blocks[i] = new Block(unpacker.unpackInt(), unpacker.unpackInt(), unpacker.unpackInt()); + } + + blockData = new PrefabBlockData(width, height, blocks); + Files.write(blocksFile.toPath(), ZipUtils.deflateBytes(mapper.writeValueAsBytes(blockData))); + legacyBlocksFile.delete(); + } else { + blockData = mapper.readValue(ZipUtils.inflateBytes(Files.readAllBytes(blocksFile.toPath())), PrefabBlockData.class); + } + + PrefabConfig config = JsonHelper.readValue(new File(file, "config.json"), PrefabConfig.class); + prefabs.put(name, new Prefab(config, blockData)); } catch(Exception e) { logger.error("Could not load prefab {}:", name, e); } } - public void registerPrefab(String name, Prefab structure) throws Exception { + public void addPrefab(String name, Prefab prefab) throws Exception { if(prefabs.containsKey(name)) { logger.warn("Duplicate prefab name: {}", name); return; } - savePrefab(name, structure); - prefabs.put(name, structure); - } - - private void savePrefab(String name, Prefab structure) throws Exception { - File outputDir = new File(dataDir, name); - outputDir.mkdirs(); - MessagePackHelper.writeToFile(new File(outputDir, "blocks.cmp"), structure); - JsonHelper.writeValue(new File(outputDir, "config.json"), structure); + File prefabDir = new File(dataDir, name); + prefabDir.mkdirs(); + JsonHelper.writeValue(new File(prefabDir, "config.json"), new PrefabConfig(prefab)); + Files.write(new File(prefabDir, "blocks.dat").toPath(), ZipUtils.deflateBytes(mapper.writeValueAsBytes(new PrefabBlockData(prefab)))); + prefabs.put(name, prefab); } public Prefab getPrefab(String name) { diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/BlockDeserializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/BlockDeserializer.java new file mode 100644 index 0000000..d35cde9 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/BlockDeserializer.java @@ -0,0 +1,28 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import brainwine.gameserver.zone.Block; + +public class BlockDeserializer extends StdDeserializer { + + public static final BlockDeserializer INSTANCE = new BlockDeserializer(); + private static final long serialVersionUID = 4595727432327616509L; + + protected BlockDeserializer() { + super(Block.class); + } + + @Override + public Block deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { + int base = parser.getIntValue(); + int back = parser.nextIntValue(0); + int front = parser.nextIntValue(0); + return new Block(base, back, front); + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/BlockSerializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/BlockSerializer.java new file mode 100644 index 0000000..fb2cda3 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/BlockSerializer.java @@ -0,0 +1,26 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import brainwine.gameserver.zone.Block; + +public class BlockSerializer extends StdSerializer { + + public static final BlockSerializer INSTANCE = new BlockSerializer(); + private static final long serialVersionUID = 4060486562629926309L; + + protected BlockSerializer() { + super(Block.class); + } + + @Override + public void serialize(Block block, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeNumber(block.getBase()); + generator.writeNumber(block.getBack()); + generator.writeNumber(block.getFront()); + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/ItemCodeSerializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/ItemCodeSerializer.java new file mode 100644 index 0000000..4db5e1e --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/ItemCodeSerializer.java @@ -0,0 +1,24 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import brainwine.gameserver.item.Item; + +public class ItemCodeSerializer extends StdSerializer { + + public static final ItemCodeSerializer INSTANCE = new ItemCodeSerializer(); + private static final long serialVersionUID = 8938614385421916365L; + + protected ItemCodeSerializer() { + super(Item.class); + } + + @Override + public void serialize(Item item, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeNumber(item.getId()); + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/MessageSerializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/MessageSerializer.java new file mode 100644 index 0000000..64cc755 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/MessageSerializer.java @@ -0,0 +1,53 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import brainwine.gameserver.server.Message; + +public class MessageSerializer extends StdSerializer { + + public static final MessageSerializer INSTANCE = new MessageSerializer(); + private static final long serialVersionUID = 2310652788158728087L; + + protected MessageSerializer() { + super(Message.class); + } + + @Override + public void serialize(Message message, JsonGenerator generator, SerializerProvider serializers) throws IOException { + Field[] fields = message.getClass().getFields(); + + try { + if(message.isPrepacked()) { + if(fields.length != 1 || !Collection.class.isAssignableFrom(fields[0].getType())) { + throw new IOException("Invalid prepacked message."); + } + + generator.writeObject(fields[0].get(message)); + } else { + List fieldValues = new ArrayList<>(); + + for(Field field : fields) { + fieldValues.add(field.get(message)); + } + + if(message.isCollection()) { + generator.writeObject(Arrays.asList(fieldValues)); + } else { + generator.writeObject(fieldValues); + } + } + } catch(IllegalArgumentException | IllegalAccessException e) { + throw new IOException(e); + } + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/NetworkChunkSerializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/NetworkChunkSerializer.java new file mode 100644 index 0000000..bbf0408 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/NetworkChunkSerializer.java @@ -0,0 +1,30 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import brainwine.gameserver.zone.Chunk; + +public class NetworkChunkSerializer extends StdSerializer { + + public static final NetworkChunkSerializer INSTANCE = new NetworkChunkSerializer(); + private static final long serialVersionUID = -1573014029866696503L; + + protected NetworkChunkSerializer() { + super(Chunk.class); + } + + @Override + public void serialize(Chunk chunk, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeStartArray(); + generator.writeNumber(chunk.getX()); + generator.writeNumber(chunk.getY()); + generator.writeNumber(chunk.getWidth()); + generator.writeNumber(chunk.getHeight()); + generator.writeObject(chunk.getBlocks()); + generator.writeEndArray(); + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializer.java b/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializer.java new file mode 100644 index 0000000..622a959 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializer.java @@ -0,0 +1,60 @@ +package brainwine.gameserver.serialization; + +import java.io.IOException; +import java.lang.reflect.Field; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import brainwine.gameserver.server.OptionalField; +import brainwine.gameserver.server.Request; + +public class RequestDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 42527921659694141L; + + public RequestDeserializer(Class type) { + super(type); + } + + @Override + public Request deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { + try { + Request request = (Request)_valueClass.newInstance(); + Field[] fields = request.getClass().getFields(); + + if(parser.currentToken() != JsonToken.START_ARRAY) { + throw new IOException("Got invalid token, expected START_ARRAY"); + } + + for(Field field : fields) { + boolean required = field.getAnnotation(OptionalField.class) == null; + JsonToken token = parser.nextToken(); + + if(token == JsonToken.VALUE_NULL) { + if(required) { + throw new IOException("Value is null, but field is required!"); + } + + continue; + } else if(token == JsonToken.END_ARRAY) { + if(required) { + throw new IOException("Array is end, but field is required!"); + } + + break; + } + + Object value = context.findRootValueDeserializer(context.constructType(field.getGenericType())).deserialize(parser, context); + field.set(request, value); + } + + return request; + } catch (InstantiationException | IllegalAccessException e) { + throw new IOException(e); + } + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializerModifier.java b/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializerModifier.java new file mode 100644 index 0000000..85eea2f --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/serialization/RequestDeserializerModifier.java @@ -0,0 +1,27 @@ +package brainwine.gameserver.serialization; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; + +import brainwine.gameserver.server.Request; + +public class RequestDeserializerModifier extends BeanDeserializerModifier { + + public static final RequestDeserializerModifier INSTANCE = new RequestDeserializerModifier(); + + private RequestDeserializerModifier() {} + + @Override + @SuppressWarnings("unchecked") + public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { + Class clazz = beanDesc.getBeanClass(); + + if(Request.class.isAssignableFrom(clazz)) { + return new RequestDeserializer((Class)clazz); + } + + return deserializer; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/server/Message.java b/gameserver/src/main/java/brainwine/gameserver/server/Message.java index afbf589..5990f9d 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/Message.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/Message.java @@ -1,43 +1,10 @@ package brainwine.gameserver.server; -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.msgpack.packer.Packer; - /** * Messages are outgoing packets to the client. */ public abstract class Message { - public void pack(Packer packer) throws IOException, IllegalArgumentException, IllegalAccessException { - Field[] fields = getClass().getFields(); - - if(isPrepacked()) { - if(fields.length != 1 || !Collection.class.isAssignableFrom(fields[0].getType())) { - throw new IOException("Prepacked messages may only contain 1 field that must be a Collection."); - } - - packer.write(fields[0].get(this)); - } else { - List data = new ArrayList<>(); - - for(Field field : fields) { - data.add(field.get(this)); - } - - if(isCollection()) { - packer.write(Arrays.asList(data)); - } else { - packer.write(data); - } - } - } - public boolean isJson() { return false; } diff --git a/gameserver/src/main/java/brainwine/gameserver/server/NetworkRegistry.java b/gameserver/src/main/java/brainwine/gameserver/server/NetworkRegistry.java index e6fb412..c35a57c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/NetworkRegistry.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/NetworkRegistry.java @@ -140,12 +140,8 @@ public class NetworkRegistry { requests.put(id, type); } - public static Request instantiateRequest(int id) throws InstantiationException, IllegalAccessException { - if(!requests.containsKey(id)) { - return null; - } - - return requests.get(id).newInstance(); + public static Class getRequestClass(int id){ + return requests.get(id); } public static void registerMessage(Class type, int id) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/Request.java b/gameserver/src/main/java/brainwine/gameserver/server/Request.java index 6008cb3..b5c77ac 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/Request.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/Request.java @@ -1,13 +1,6 @@ package brainwine.gameserver.server; -import java.io.IOException; -import java.lang.reflect.Field; - -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - import brainwine.gameserver.server.pipeline.Connection; -import brainwine.gameserver.server.requests.BlocksIgnoreRequest; /** * Requests are incoming packets from the client. @@ -15,30 +8,4 @@ import brainwine.gameserver.server.requests.BlocksIgnoreRequest; public abstract class Request { public abstract void process(Connection connection); - - /** - * Can be overriden for custom unpacking rules, as seen in {@link BlocksIgnoreRequest} - */ - public void unpack(Unpacker unpacker) throws IllegalArgumentException, IllegalAccessException, IOException { - unpacker.readArrayBegin(); - Field[] fields = this.getClass().getFields(); - - for(Field field : fields) { - try { - if(unpacker.getNextType() == ValueType.NIL && field.getAnnotation(OptionalField.class) == null) { - throw new IOException("Value is nil, but field is required!"); - } - } catch(Exception e) { - if(field.getAnnotation(OptionalField.class) == null) { - throw e; - } - - continue; - } - - field.set(this, unpacker.read(field.getType())); - } - - unpacker.readArrayEnd(true); - } } diff --git a/gameserver/src/main/java/brainwine/gameserver/server/Server.java b/gameserver/src/main/java/brainwine/gameserver/server/Server.java index 6d68c36..0841174 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/Server.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/Server.java @@ -7,7 +7,22 @@ import java.util.concurrent.ThreadFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.msgpack.core.MessagePack; +import org.msgpack.jackson.dataformat.MessagePackFactory; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import brainwine.gameserver.serialization.BlockSerializer; +import brainwine.gameserver.serialization.ItemCodeSerializer; +import brainwine.gameserver.serialization.MessageSerializer; +import brainwine.gameserver.serialization.NetworkChunkSerializer; +import brainwine.gameserver.serialization.RequestDeserializerModifier; import brainwine.gameserver.server.pipeline.Connection; import brainwine.gameserver.server.pipeline.MessageEncoder; import brainwine.gameserver.server.pipeline.RequestDecoder; @@ -32,6 +47,20 @@ public class Server { private static final Logger logger = LogManager.getLogger(); private static final ThreadFactory threadFactory = new DefaultThreadFactory("netty"); + private static final ObjectMapper mapper = new ObjectMapper(new MessagePackFactory( + MessagePack.DEFAULT_PACKER_CONFIG.withStr8FormatSupport(false))) + .configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true) + .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true) + .registerModule(new SimpleModule() + .setDeserializerModifier(RequestDeserializerModifier.INSTANCE) + .addSerializer(MessageSerializer.INSTANCE) + .addSerializer(BlockSerializer.INSTANCE) + .addSerializer(NetworkChunkSerializer.INSTANCE) + .addSerializer(ItemCodeSerializer.INSTANCE)); + private static final ObjectWriter writer = mapper.writer(); + private static final ObjectReader reader = mapper.reader(); private final List endpoints = new ArrayList<>(); private final Class channelType; private final EventLoopGroup eventLoopGroup; @@ -55,8 +84,8 @@ public class Server { protected void initChannel(Channel channel) throws Exception { Connection connection = new Connection(); channel.pipeline().addLast("framer", new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 1, 4, 0, 0, true)); - channel.pipeline().addLast("encoder", new MessageEncoder(connection)); - channel.pipeline().addLast("decoder", new RequestDecoder()); + channel.pipeline().addLast("encoder", new MessageEncoder(writer, connection)); + channel.pipeline().addLast("decoder", new RequestDecoder(reader)); channel.pipeline().addLast("handler", connection); } }).bind(port).syncUninterruptibly()); diff --git a/gameserver/src/main/java/brainwine/gameserver/server/pipeline/MessageEncoder.java b/gameserver/src/main/java/brainwine/gameserver/server/pipeline/MessageEncoder.java index 2a8d5e8..f11292b 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/pipeline/MessageEncoder.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/pipeline/MessageEncoder.java @@ -5,9 +5,8 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import org.msgpack.packer.BufferPacker; +import com.fasterxml.jackson.databind.ObjectWriter; -import brainwine.gameserver.msgpack.MessagePackHelper; import brainwine.gameserver.server.Message; import brainwine.gameserver.server.NetworkRegistry; import brainwine.gameserver.util.ZipUtils; @@ -18,9 +17,11 @@ import io.netty.handler.codec.MessageToByteEncoder; public class MessageEncoder extends MessageToByteEncoder { + private final ObjectWriter writer; private final Connection connection; - public MessageEncoder(Connection connection) { + public MessageEncoder(ObjectWriter writer, Connection connection) { + this.writer = writer; this.connection = connection; } @@ -45,10 +46,7 @@ public class MessageEncoder extends MessageToByteEncoder { bytes = JsonHelper.writeValueAsBytes(data); } else { - BufferPacker packer = MessagePackHelper.createBufferPacker(); - in.pack(packer); - bytes = packer.toByteArray(); - packer.close(); + bytes = writer.writeValueAsBytes(in); } if(in.isCompressed()) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/pipeline/RequestDecoder.java b/gameserver/src/main/java/brainwine/gameserver/server/pipeline/RequestDecoder.java index 913d105..511050f 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/pipeline/RequestDecoder.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/pipeline/RequestDecoder.java @@ -3,17 +3,22 @@ package brainwine.gameserver.server.pipeline; import java.io.IOException; import java.util.List; -import org.msgpack.unpacker.Unpacker; +import com.fasterxml.jackson.databind.ObjectReader; -import brainwine.gameserver.msgpack.MessagePackHelper; -import brainwine.gameserver.server.Request; import brainwine.gameserver.server.NetworkRegistry; +import brainwine.gameserver.server.Request; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; public class RequestDecoder extends MessageToMessageDecoder { - + + private final ObjectReader reader; + + public RequestDecoder(ObjectReader reader) { + this.reader = reader; + } + @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) throws Exception { int id = buf.readByte() & 0xFF; @@ -23,17 +28,15 @@ public class RequestDecoder extends MessageToMessageDecoder { throw new IOException("Request exceeds max length of 1024 bytes"); } - Request request = NetworkRegistry.instantiateRequest(id); + Class type = NetworkRegistry.getRequestClass(id); - if(request == null) { + if(type == null) { throw new IOException("Client sent invalid request: " + id); } byte[] bytes = new byte[length]; buf.readBytes(bytes); - Unpacker unpacker = MessagePackHelper.createBufferUnpacker(bytes); - request.unpack(unpacker); - unpacker.close(); + Request request = reader.readValue(bytes, type); out.add(request); } } diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/AuthenticateRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/AuthenticateRequest.java index 42d50e2..6768e7d 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/AuthenticateRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/AuthenticateRequest.java @@ -1,7 +1,5 @@ package brainwine.gameserver.server.requests; -import org.msgpack.type.Value; - import brainwine.gameserver.GameServer; import brainwine.gameserver.server.OptionalField; import brainwine.gameserver.server.Request; @@ -14,7 +12,7 @@ public class AuthenticateRequest extends Request { public String authToken; @OptionalField - public Value details; + public Object details; @Override public void process(Connection connection) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockUseRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockUseRequest.java index a89e985..18ec86c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockUseRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockUseRequest.java @@ -14,7 +14,6 @@ import brainwine.gameserver.item.ItemUseType; import brainwine.gameserver.item.Layer; import brainwine.gameserver.loot.Loot; import brainwine.gameserver.loot.LootManager; -import brainwine.gameserver.msgpack.models.BlockUseData; import brainwine.gameserver.server.OptionalField; import brainwine.gameserver.server.PlayerRequest; import brainwine.gameserver.util.MapHelper; @@ -30,7 +29,7 @@ public class BlockUseRequest extends PlayerRequest { public Layer layer; @OptionalField - public BlockUseData data; + public Object[] data; @Override public void process(Player player) { @@ -40,7 +39,10 @@ public class BlockUseRequest extends PlayerRequest { return; } - Object[] data = this.data == null ? null : this.data.getData(); + if(data != null && data.length == 1 && data[0] instanceof Map) { + data = ((Map)data[0]).values().toArray(); + } + Block block = zone.getBlock(x, y); MetaBlock metaBlock = zone.getMetaBlock(x, y); Item item = block.getItem(layer); diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlocksIgnoreRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlocksIgnoreRequest.java index 8162c9a..e702028 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlocksIgnoreRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlocksIgnoreRequest.java @@ -1,10 +1,5 @@ package brainwine.gameserver.server.requests; -import java.io.IOException; - -import org.msgpack.type.ValueType; -import org.msgpack.unpacker.Unpacker; - import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.server.PlayerRequest; @@ -15,23 +10,6 @@ public class BlocksIgnoreRequest extends PlayerRequest { public int[] chunkIndexes; - @Override - public void unpack(Unpacker unpacker) throws IOException { - int length = unpacker.readArrayBegin(); - - if(unpacker.getNextType() == ValueType.INTEGER) { - chunkIndexes = new int[length]; - - for(int i = 0; i < length; i++) { - chunkIndexes[i] = unpacker.readInt(); - } - } else { - chunkIndexes = unpacker.read(int[].class); - } - - unpacker.readArrayEnd(); - } - @Override public void process(Player player) { /** diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/ChangeAppearanceRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/ChangeAppearanceRequest.java index bb6bd9b..0d6f87b 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/ChangeAppearanceRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/ChangeAppearanceRequest.java @@ -11,7 +11,6 @@ import brainwine.gameserver.entity.player.ColorSlot; import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.item.Item; import brainwine.gameserver.item.ItemRegistry; -import brainwine.gameserver.msgpack.models.AppearanceData; import brainwine.gameserver.server.PlayerRequest; import brainwine.gameserver.server.messages.DialogMessage; import brainwine.gameserver.util.MapHelper; @@ -22,7 +21,7 @@ import brainwine.gameserver.util.MapHelper; */ public class ChangeAppearanceRequest extends PlayerRequest { - public AppearanceData data; + public Map data; @Override public void process(Player player) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/DialogRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/DialogRequest.java index 82dc77d..75044a5 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/DialogRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/DialogRequest.java @@ -1,19 +1,28 @@ package brainwine.gameserver.server.requests; +import java.util.Map; + import brainwine.gameserver.entity.player.Player; -import brainwine.gameserver.msgpack.models.DialogInputData; import brainwine.gameserver.server.PlayerRequest; public class DialogRequest extends PlayerRequest { - public DialogInputData data; + public Object id; + public Object[] input; @Override public void process(Player player) { - if(data.isType1()) { + if(input.length == 1 && input[0] instanceof Map) { + input = ((Map)input[0]).values().toArray(); + } + + if(id instanceof String) { player.alert("Sorry, this action is not implemented yet."); - } else if(data.isType2()) { - player.handleDialogInput(data.getDialogId(), data.getInputData()); + return; + } else if(id instanceof Integer) { + if((int)id > 0) { + player.handleDialogInput((int)id, input); + } } } } diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/EventRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/EventRequest.java index f864e79..54b5947 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/EventRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/EventRequest.java @@ -1,14 +1,12 @@ package brainwine.gameserver.server.requests; -import org.msgpack.type.Value; - import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.server.PlayerRequest; public class EventRequest extends PlayerRequest { public String key; - public Value value; + public Object value; @Override public void process(Player player) {} diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/InventoryUseRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/InventoryUseRequest.java index ce79068..55e4919 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/InventoryUseRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/InventoryUseRequest.java @@ -1,7 +1,5 @@ package brainwine.gameserver.server.requests; -import org.msgpack.type.Value; - import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.item.Item; import brainwine.gameserver.server.OptionalField; @@ -19,7 +17,7 @@ public class InventoryUseRequest extends PlayerRequest { public int status; // 0 = select, 1 = start, 2 = stop @OptionalField - public Value details; // array + public Object details; // array @Override public void process(Player player) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/RespawnRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/RespawnRequest.java index 01faf75..6b90b29 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/RespawnRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/RespawnRequest.java @@ -1,13 +1,11 @@ package brainwine.gameserver.server.requests; -import org.msgpack.type.Value; - import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.server.PlayerRequest; public class RespawnRequest extends PlayerRequest { - public Value status; + public Object status; @Override public void process(Player player) { diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/StatusRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/StatusRequest.java index 2c9f73d..ffc0f27 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/StatusRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/StatusRequest.java @@ -1,13 +1,11 @@ package brainwine.gameserver.server.requests; -import org.msgpack.type.Value; - import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.server.PlayerRequest; public class StatusRequest extends PlayerRequest { - public Value status; + public Object status; @Override public void process(Player player) { diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/Biome.java b/gameserver/src/main/java/brainwine/gameserver/zone/Biome.java index 420fece..f1bb015 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/Biome.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/Biome.java @@ -4,10 +4,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import com.fasterxml.jackson.annotation.JsonValue; -import brainwine.gameserver.msgpack.EnumValue; -import brainwine.gameserver.msgpack.RegisterEnum; - -@RegisterEnum public enum Biome { @JsonEnumDefaultValue @@ -20,7 +16,6 @@ public enum Biome { SPACE; @JsonValue - @EnumValue public String getId() { return toString().toLowerCase(); } diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/Chunk.java b/gameserver/src/main/java/brainwine/gameserver/zone/Chunk.java index 2f9689b..954ddd7 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/Chunk.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/Chunk.java @@ -1,5 +1,9 @@ package brainwine.gameserver.zone; +import java.beans.ConstructorProperties; + +import com.fasterxml.jackson.annotation.JsonIgnore; + /** * Simple & convenient class to store block data. * Outside of allowing zones to be chopped up unto chunks, it doesn't @@ -14,12 +18,17 @@ public class Chunk { private final Block[] blocks; private boolean modified; - public Chunk(int x, int y, int width, int height) { + @ConstructorProperties({"x", "y", "width", "height", "blocks"}) + public Chunk(int x, int y, int width, int height, Block[] blocks) { this.x = x; this.y = y; this.width = width; this.height = height; - this.blocks = new Block[width * height]; + this.blocks = blocks; + } + + public Chunk(int x, int y, int width, int height) { + this(x, y, width, height, new Block[width * height]); for(int i = 0; i < blocks.length; i++) { blocks[i] = new Block(); @@ -30,6 +39,7 @@ public class Chunk { this.modified = modified; } + @JsonIgnore public boolean isModified() { return modified; } @@ -50,16 +60,6 @@ public class Chunk { return height; } - public void setBlock(int x, int y, Block block) { - setBlock(getBlockIndex(x % width, y % height), block); - } - - public void setBlock(int index, Block block) { - if(isIndexInBounds(index)) { - blocks[index] = block; - } - } - public Block getBlock(int x, int y) { return getBlock(getBlockIndex(x % width, y % height)); } diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java b/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java index dc3bc8d..817c270 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java @@ -1,134 +1,104 @@ package brainwine.gameserver.zone; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.file.Files; -import java.util.Arrays; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.msgpack.unpacker.Unpacker; +import org.msgpack.core.MessagePack; +import org.msgpack.core.MessageUnpacker; +import org.msgpack.jackson.dataformat.MessagePackFactory; -import brainwine.gameserver.msgpack.MessagePackHelper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import brainwine.gameserver.serialization.BlockDeserializer; +import brainwine.gameserver.serialization.BlockSerializer; import brainwine.gameserver.util.ZipUtils; public class ChunkManager { - + private static final Logger logger = LogManager.getLogger(); - private static final String headerString = "brainwine blocks file"; - private static final int latestVersion = 1; - private static final int dataOffset = headerString.length() + 4; private static final int allocSize = 2048; - private static boolean conversionNotified; + private static final ObjectMapper mapper = new ObjectMapper(new MessagePackFactory()) + .registerModule(new SimpleModule() + .addDeserializer(Block.class, BlockDeserializer.INSTANCE) + .addSerializer(BlockSerializer.INSTANCE)); private final Zone zone; + private final File blocksFile; private RandomAccessFile file; + private int dataOffset; public ChunkManager(Zone zone) { this.zone = zone; + blocksFile = new File(zone.getDirectory(), "blocks.dat"); + File legacyBlocksFile = new File(zone.getDirectory(), "blocks"); - try { - if(file == null) { - File blocksFile = new File(zone.getDirectory(), "blocks.dat"); - File legacyBlocksFile = new File(zone.getDirectory(), "blocks"); + if(!blocksFile.exists() && legacyBlocksFile.exists()) { + logger.info("Updating blocks file for zone {} ...", zone.getDocumentId()); + DataInputStream inputStream = null; + DataOutputStream outputStream = null; + + try { + inputStream = new DataInputStream(new FileInputStream(legacyBlocksFile)); + outputStream = new DataOutputStream(new FileOutputStream(blocksFile)); + int chunkCount = zone.getChunkCount(); - if(!blocksFile.exists()) { - blocksFile.getParentFile().mkdirs(); - blocksFile.createNewFile(); - } - - file = new RandomAccessFile(blocksFile, "rw"); - - if(file.length() == 0) { - file.writeUTF(headerString); - file.writeInt(latestVersion); + for(int i = 0; i < chunkCount; i++) { + short length = inputStream.readShort(); + byte[] chunkBytes = new byte[length]; + inputStream.read(chunkBytes); + inputStream.skipBytes(2048 - length - 2); + chunkBytes = ZipUtils.inflateBytes(chunkBytes); + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(chunkBytes); + unpacker.unpackArrayHeader(); + int x = unpacker.unpackInt(); + int y = unpacker.unpackInt(); + int width = unpacker.unpackInt(); + int height = unpacker.unpackInt(); + Block[] blocks = new Block[unpacker.unpackArrayHeader() / 3]; - if(legacyBlocksFile.exists()) { - if(!conversionNotified) { - logger.info("One or more block data files need to be converted. This might take a while ..."); - conversionNotified = true; - } - - convertLegacyBlocksFile(legacyBlocksFile); - } - } else { - if(!file.readUTF().equals(headerString)) { - throw new IOException("Invalid header string"); + for(int j = 0; j < blocks.length; j++) { + blocks[j] = new Block(unpacker.unpackInt(), unpacker.unpackInt(), unpacker.unpackInt()); } + + unpacker.close(); + byte[] bytes = ZipUtils.deflateBytes(mapper.writeValueAsBytes(new Chunk(x, y, width, height, blocks))); + outputStream.writeShort(bytes.length); + outputStream.write(bytes); + outputStream.write(new byte[allocSize - bytes.length - 2]); } - } - } catch(Exception e) { - logger.error("ChunkManager construction for zone {} failed", zone.getDocumentId(), e); - } - } - - private void convertLegacyBlocksFile(File legacyBlocksFile) throws Exception { - byte[] bytes = Files.readAllBytes(legacyBlocksFile.toPath()); - - for(int i = 0; i < bytes.length; i += 2048) { - short length = (short)(((bytes[i] & 0xFF) << 8) + (bytes[i + 1] & 0xFF)); - byte[] chunkBytes = ZipUtils.inflateBytes(Arrays.copyOfRange(bytes, i + 2, i + 2 + length)); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - Unpacker unpacker = MessagePackHelper.createBufferUnpacker(chunkBytes); - unpacker.readArrayBegin(); - int x = unpacker.readInt(); - int y = unpacker.readInt(); - int width = unpacker.readInt(); - int height = unpacker.readInt(); - dos.writeInt(x); - dos.writeInt(y); - dos.writeInt(width); - dos.writeInt(height); - unpacker.readArrayBegin(); - - for(int j = 0; j < width * height; j++) { - dos.writeInt(unpacker.readInt()); - dos.writeInt(unpacker.readInt()); - dos.writeInt(unpacker.readInt()); + + inputStream.close(); + outputStream.close(); + } catch(Exception e) { + logger.error("Could not update blocks file for zone {}", zone.getDocumentId(), e); } - unpacker.close(); - byte[] updatedBytes = ZipUtils.deflateBytes(baos.toByteArray()); - dos.close(); - file.seek(dataOffset + zone.getChunkIndex(x, y) * allocSize); - file.writeShort(updatedBytes.length); - file.write(updatedBytes); + legacyBlocksFile.delete(); } } public Chunk loadChunk(int index) { - Chunk chunk = null; - DataInputStream dis = null; - try { + if(file == null) { + file = new RandomAccessFile(blocksFile, "rw"); + } + file.seek(dataOffset + index * allocSize); byte[] bytes = new byte[file.readShort()]; file.read(bytes); - - dis = new DataInputStream(new ByteArrayInputStream(ZipUtils.inflateBytes(bytes))); - chunk = new Chunk(dis.readInt(), dis.readInt(), dis.readInt(), dis.readInt()); - - for(int i = 0; i < zone.getChunkWidth() * zone.getChunkHeight(); i++) { - chunk.setBlock(i, new Block(dis.readInt(), dis.readInt(), dis.readInt())); - } + return mapper.readValue(ZipUtils.inflateBytes(bytes), Chunk.class); } catch(Exception e) { logger.error("Could not load chunk {} of zone {}", index, zone.getDocumentId(), e); - } finally { - if(dis != null) { - try { - dis.close(); - } catch (IOException e) { - logger.warn("Resource could not be closed", e); - } - } } - return chunk; + return null; } public void saveModifiedChunks() { @@ -140,38 +110,25 @@ public class ChunkManager { } public void saveChunk(Chunk chunk) { - DataOutputStream dos = null; int index = zone.getChunkIndex(chunk.getX(), chunk.getY()); try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(allocSize); - dos = new DataOutputStream(baos); - dos.writeInt(chunk.getX()); - dos.writeInt(chunk.getY()); - dos.writeInt(chunk.getWidth()); - dos.writeInt(chunk.getHeight()); - - for(Block block : chunk.getBlocks()) { - dos.writeInt(block.getBase()); - dos.writeInt(block.getBack()); - dos.writeInt(block.getFront()); + if(file == null) { + file = new RandomAccessFile(blocksFile, "rw"); + } + + byte[] bytes = ZipUtils.deflateBytes(mapper.writeValueAsBytes(chunk)); + + if(bytes.length > allocSize) { + throw new IOException("WARNING: bigger than alloc size: " + bytes.length); } - byte[] bytes = ZipUtils.deflateBytes(baos.toByteArray()); file.seek(dataOffset + index * allocSize); file.writeShort(bytes.length); file.write(bytes); chunk.setModified(false); - } catch(Exception e) { - logger.error("Could not save chunk %s of zone %s", index, zone.getDocumentId(), e); - } finally { - if(dos != null) { - try { - dos.close(); - } catch (IOException e) { - logger.warn("Resource could not be closed", e); - } - } + } catch (IOException e) { + logger.error("Could not save chunk {} of zone {}", index, zone.getDocumentId(), e); } } } diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/SizePreset.java b/gameserver/src/main/java/brainwine/gameserver/zone/SizePreset.java deleted file mode 100644 index 0ac9288..0000000 --- a/gameserver/src/main/java/brainwine/gameserver/zone/SizePreset.java +++ /dev/null @@ -1,24 +0,0 @@ -package brainwine.gameserver.zone; - -public enum SizePreset { - - MINI(1000, 400), - NORMAL(2000, 800), - XL(4000, 1600); - - private final int width; - private final int height; - - private SizePreset(int width, int height) { - this.width = width; - this.height = height; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } -} diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java b/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java index 8073878..e57b68c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java @@ -1,6 +1,5 @@ package brainwine.gameserver.zone; -import java.beans.ConstructorProperties; import java.io.File; import java.util.ArrayDeque; import java.util.ArrayList; @@ -19,12 +18,7 @@ import java.util.Set; import java.util.UUID; import java.util.function.Predicate; -import org.msgpack.unpacker.BufferUnpacker; - -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIncludeProperties; -import com.fasterxml.jackson.annotation.JsonValue; import brainwine.gameserver.GameServer; import brainwine.gameserver.entity.Entity; @@ -38,7 +32,6 @@ import brainwine.gameserver.item.ItemUseType; import brainwine.gameserver.item.Layer; import brainwine.gameserver.item.MetaType; import brainwine.gameserver.item.ModType; -import brainwine.gameserver.msgpack.MessagePackHelper; import brainwine.gameserver.prefab.Prefab; import brainwine.gameserver.server.Message; import brainwine.gameserver.server.messages.BlockChangeMessage; @@ -52,9 +45,7 @@ import brainwine.gameserver.server.messages.ZoneExploredMessage; import brainwine.gameserver.server.messages.ZoneStatusMessage; import brainwine.gameserver.util.MapHelper; import brainwine.gameserver.util.MathUtils; -import brainwine.shared.JsonHelper; -@JsonIncludeProperties({"name", "biome", "width", "height"}) public class Zone { public static final int DEFAULT_CHUNK_WIDTH = 20; @@ -64,8 +55,8 @@ public class Zone { private final Biome biome; private final int width; private final int height; - private final int chunkWidth; - private final int chunkHeight; + private final int chunkWidth = DEFAULT_CHUNK_WIDTH; + private final int chunkHeight = DEFAULT_CHUNK_HEIGHT; private final int numChunksWidth; private final int numChunksHeight; private final int[] surface; @@ -88,23 +79,19 @@ public class Zone { private final Map globalMetaBlocks = new HashMap<>(); private final Map fieldBlocks = new HashMap<>(); - public Zone(String documentId, String name, Biome biome, SizePreset sizePreset) { - this(documentId, name, biome, sizePreset.getWidth(), sizePreset.getHeight()); + protected Zone(String documentId, ZoneConfig config, ZoneData data) { + this(documentId, config.getName(), config.getBiome(), config.getWidth(), config.getHeight()); + surface = data.getSurface(); + sunlight = data.getSunlight(); + chunksExplored = data.getChunksExplored(); } - @ConstructorProperties({"documentId", "name", "biome", "width", "height"}) - public Zone(@JacksonInject("documentId") String documentId, String name, Biome biome, int width, int height) { - this(documentId, name, biome, width, height, DEFAULT_CHUNK_WIDTH, DEFAULT_CHUNK_HEIGHT); - } - - private Zone(String documentId, String name, Biome biome, int width, int height, int chunkWidth, int chunkHeight) { + public Zone(String documentId, String name, Biome biome, int width, int height) { this.documentId = documentId; this.name = name; this.biome = biome; this.width = width; this.height = height; - this.chunkWidth = chunkWidth; - this.chunkHeight = chunkHeight; numChunksWidth = width / chunkWidth; numChunksHeight = height / chunkHeight; surface = new int[width]; @@ -139,29 +126,14 @@ public class Zone { } } } + + lastTick = now; + ticks++; } - public void load() throws Exception { - pendingSunlight.clear(); - File dataDir = new File("zones", documentId); - File shapeFile = new File(dataDir, "shape.cmp"); - BufferUnpacker unpacker = MessagePackHelper.readFiles(shapeFile); - unpacker.read(surface); - unpacker.read(sunlight); - pendingSunlight.addAll(Arrays.asList(unpacker.read(Integer[].class))); - unpacker.read(chunksExplored); - setMetaBlocks(JsonHelper.readList(new File(dataDir, "metablocks.json"), MetaBlock.class)); - indexDungeons(); - } - - public void save() throws Exception { - File dataDir = new File("zones", documentId); - dataDir.mkdirs(); - JsonHelper.writeValue(new File(dataDir, "config.json"), this); + public void saveModifiedChunks() { chunkManager.saveModifiedChunks(); removeInactiveChunks(); - MessagePackHelper.writeToFile(new File(dataDir, "shape.cmp"), surface, sunlight, pendingSunlight, chunksExplored); - JsonHelper.writeValue(new File(dataDir, "metablocks.json"), metaBlocks.values()); } /** @@ -615,6 +587,8 @@ public class Zone { indexMetaBlock(getBlockIndex(x, y), metaBlock); } } + + indexDungeons(); } private boolean ownsMetaBlock(MetaBlock metaBlock, Player player) { @@ -914,16 +888,6 @@ public class Zone { return numChunksWidth * numChunksHeight; } - @JsonValue - protected Map getJsonConfig() { - Map config = new HashMap<>(); - config.put("name", name); - config.put("biome", biome); - config.put("width", width); - config.put("height", height); - return config; - } - /** * @return A {@link Map} containing all the data necessary for use in {@link ConfigurationMessage}. */ diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/ZoneConfig.java b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneConfig.java new file mode 100644 index 0000000..696ddf4 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneConfig.java @@ -0,0 +1,38 @@ +package brainwine.gameserver.zone; + +import java.beans.ConstructorProperties; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ZoneConfig { + + private final String name; + private final Biome biome; + private final int width; + private final int height; + + @ConstructorProperties({"name", "biome", "width", "height"}) + public ZoneConfig(String name, Biome biome, int width, int height) { + this.name = name; + this.biome = biome; + this.width = width; + this.height = height; + } + + public String getName() { + return name; + } + + public Biome getBiome() { + return biome; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/ZoneData.java b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneData.java new file mode 100644 index 0000000..8f4dac6 --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneData.java @@ -0,0 +1,38 @@ +package brainwine.gameserver.zone; + +import java.beans.ConstructorProperties; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ZoneData { + + private int[] surface; + private int[] sunlight; + private int[] pendingSunlight; + private boolean[] chunksExplored; + + @ConstructorProperties({"surface", "sunlight", "pending_sunlight", "chunks_explored"}) + public ZoneData(int[] surface, int[] sunlight, int[] pendingSunlight, boolean[] chunksExplored) { + this.surface = surface; + this.sunlight = sunlight; + this.pendingSunlight = pendingSunlight; + this.chunksExplored = chunksExplored; + } + + public int[] getSurface() { + return surface; + } + + public int[] getSunlight() { + return sunlight; + } + + public int[] getPendingSunlight() { + return pendingSunlight; + } + + public boolean[] getChunksExplored() { + return chunksExplored; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/ZoneManager.java b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneManager.java index 5f3b13f..845030c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/ZoneManager.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/ZoneManager.java @@ -1,6 +1,7 @@ package brainwine.gameserver.zone; import java.io.File; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -13,9 +14,13 @@ import java.util.function.Predicate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.msgpack.core.MessagePack; +import org.msgpack.core.MessageUnpacker; +import org.msgpack.jackson.dataformat.MessagePackFactory; -import com.fasterxml.jackson.databind.InjectableValues; +import com.fasterxml.jackson.databind.ObjectMapper; +import brainwine.gameserver.util.ZipUtils; import brainwine.gameserver.zone.gen.AsyncZoneGenerator; import brainwine.gameserver.zone.gen.StaticZoneGenerator; import brainwine.shared.JsonHelper; @@ -24,12 +29,31 @@ public class ZoneManager { private static final Logger logger = LogManager.getLogger(); private final AsyncZoneGenerator asyncGenerator = new AsyncZoneGenerator(this); + private final ObjectMapper mapper = new ObjectMapper(new MessagePackFactory()); private final File dataDir = new File("zones"); private Map zones = new HashMap<>(); private Map zonesByName = new HashMap<>(); public ZoneManager() { - loadZones(); + logger.info("Loading zone data ..."); + dataDir.mkdirs(); + + for(File file : dataDir.listFiles()) { + if(file.isDirectory()) { + loadZone(file); + } + } + + if(zones.isEmpty()) { + logger.info("No zones were loaded. Generating default zone ..."); + Zone zone = StaticZoneGenerator.generateZone(Biome.PLAIN, 2000, 800); + saveZone(zone); + putZone(zone); + } else { + logger.info("Successfully loaded {} zone(s)", zonesByName.size()); + } + + logger.info("Starting zone generator thread ..."); asyncGenerator.setDaemon(true); asyncGenerator.start(); } @@ -40,55 +64,78 @@ public class ZoneManager { } } + private void loadZone(File file) { + String id = file.getName(); + File dataFile = new File(file, "zone.dat"); + File shapeFile = new File(file, "shape.cmp"); + + try { + ZoneData data = null; + + if(shapeFile.exists() && !dataFile.exists()) { + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(ZipUtils.inflateBytes(Files.readAllBytes(shapeFile.toPath()))); + int[] surface = new int[unpacker.unpackArrayHeader()]; + + for(int i = 0; i < surface.length; i++) { + surface[i] = unpacker.unpackInt(); + } + + int[] sunlight = new int[unpacker.unpackArrayHeader()]; + + for(int i = 0; i < sunlight.length; i++) { + sunlight[i] = unpacker.unpackInt(); + } + + int[] pendingSunlight = new int[unpacker.unpackArrayHeader()]; + + for(int i = 0; i < pendingSunlight.length; i++) { + pendingSunlight[i] = unpacker.unpackInt(); + } + + boolean[] chunksExplored = new boolean[unpacker.unpackArrayHeader()]; + + for(int i = 0; i < pendingSunlight.length; i++) { + chunksExplored[i] = unpacker.unpackBoolean(); + } + + data = new ZoneData(surface, sunlight, pendingSunlight, chunksExplored); + mapper.writeValue(dataFile, data); + shapeFile.delete(); + } else { + data = mapper.readValue(ZipUtils.inflateBytes(Files.readAllBytes(dataFile.toPath())), ZoneData.class); + } + + ZoneConfig config = JsonHelper.readValue(new File(file, "config.json"), ZoneConfig.class); + Zone zone = new Zone(id, config, data); + zone.setMetaBlocks(JsonHelper.readList(new File(file, "metablocks.json"), MetaBlock.class)); + putZone(zone); + } catch (Exception e) { + logger.error("Zone load failure. id: {}", id, e); + } + } + public void saveZones() { - for(Zone zone : zonesByName.values()) { + for(Zone zone : getZones()) { saveZone(zone); } } public void saveZone(Zone zone) { + File file = zone.getDirectory(); + file.mkdirs(); + try { - zone.save(); + zone.saveModifiedChunks(); + ZoneConfig config = JsonHelper.readValue(zone, ZoneConfig.class); + ZoneData data = JsonHelper.readValue(zone, ZoneData.class); + JsonHelper.writeValue(new File(file, "metablocks.json"), zone.getMetaBlocks()); + JsonHelper.writeValue(new File(file, "config.json"), config); + Files.write(new File(file, "zone.dat").toPath(), ZipUtils.deflateBytes(mapper.writeValueAsBytes(data))); } catch(Exception e) { logger.error("Zone save failure. id: {}", zone.getDocumentId(), e); } } - private void loadZones() { - logger.info("Loading zone data ..."); - dataDir.mkdirs(); - File[] files = dataDir.listFiles(); - - if(files.length == 0) { - logger.info("Generating default zone ..."); - Zone zone = StaticZoneGenerator.generateZone(Biome.PLAIN, 2000, 800); - saveZone(zone); - putZone(zone); - return; - } - - for(File file : files) { - if(file.isDirectory()) { - loadZone(file); - } - } - - logger.info("Successfully loaded {} zone(s)", zonesByName.size()); - } - - private void loadZone(File file) { - String id = file.getName(); - - try { - File configFile = new File(file, "config.json"); - Zone zone = JsonHelper.readValue(configFile, Zone.class, new InjectableValues.Std().addValue("documentId", id)); - zone.load(); - putZone(zone); - } catch (Exception e) { - logger.error("Zone load failure. id: {}", id, e); - } - } - private void putZone(Zone zone) { String id = zone.getDocumentId(); String name = zone.getName();