[Class] Position - Serializable Location

Discussion in 'Resources' started by Dragonphase, Jul 29, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    Dragonphase

    A simple class to serialize and deserialize locations to Configurations:

    Code:java
    1. import java.util.HashMap;
    2. import java.util.Map;
    3.  
    4. import org.bukkit.Bukkit;
    5. import org.bukkit.Location;
    6. import org.bukkit.configuration.serialization.ConfigurationSerializable;
    7.  
    8. public class Position implements ConfigurationSerializable{
    9.  
    10. private String world;
    11. private double x, y, z;
    12.  
    13. public Position(Location location){
    14. this(location.getWorld().getName(), location.getX(), location.getY(), location.getZ());
    15. }
    16.  
    17. public Position(String world, double x, double y, double z){
    18. setWorld(world);
    19. setX(x);
    20. setY(y);
    21. setZ(z);
    22. }
    23.  
    24. public String getWorld() {
    25. return world;
    26. }
    27.  
    28. public void setWorld(String world) {
    29. this.world = world;
    30. }
    31.  
    32. public double getX() {
    33. return x;
    34. }
    35.  
    36. public void setX(double x) {
    37. this.x = x;
    38. }
    39.  
    40. public double getY() {
    41. return y;
    42. }
    43.  
    44. public void setY(double y) {
    45. this.y = y;
    46. }
    47.  
    48. public double getZ() {
    49. return z;
    50. }
    51.  
    52. public void setZ(double z) {
    53. this.z = z;
    54. }
    55.  
    56. public Location getLocation(){
    57. return new Location(Bukkit.getWorld(getWorld()), getZ(), getY(), getZ());
    58. }
    59.  
    60. @Override
    61. public Map<String, Object> serialize() {
    62. Map<String, Object> result = new HashMap<String, Object>();
    63.  
    64. result.put("world", getWorld());
    65. result.put("x", getX());
    66. result.put("y", getY());
    67. result.put("z", getZ());
    68.  
    69. return result;
    70. }
    71.  
    72. public static Position deserialize(Map<String, Object> args){
    73. return new Position((String)args.get("world"), (Double)args.get("x"), (Double)args.get("y"), (Double)args.get("z"));
    74. }
    75. }
    76.  


    Don't forget to register the class in your main class:

    Code:java
    1. static{
    2. ConfigurationSerialization.registerClass(Position.class);
    3. }


    With this, you can save and load Positions to and from your .yml files!
     
  2. Offline

    xTigerRebornx

  3. Offline

    Dragonphase

    xTigerRebornx

    There's enough information in that post for you to be able to do that yourself. This just represents how to save a Location in a simple form, and how to use ConfigurationSerializable.
     
  4. Offline

    MCForger

    Dragonphase xTigerRebornx
    Here is an example I wrote up for users to look at as well:
    Code:java
    1. import java.io.File;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. import java.util.logging.Logger;
    5.  
    6. import org.bukkit.Bukkit;
    7. import org.bukkit.Location;
    8. import org.bukkit.World;
    9. import org.bukkit.configuration.file.FileConfiguration;
    10. import org.bukkit.configuration.file.YamlConfiguration;
    11. import org.bukkit.configuration.serialization.ConfigurationSerialization;
    12. import org.bukkit.plugin.java.JavaPlugin;
    13.  
    14. public class LocationSerializationPlugin extends JavaPlugin
    15. {
    16. private final String serializedLocationsPath = "serialized-locations";
    17.  
    18. private Logger logger;
    19. private File saveFile;
    20. private FileConfiguration saveConfig;
    21. private List<SerializableLocation> locations;
    22.  
    23. @Override
    24. public void onDisable()
    25. {
    26. // Creating some locations for testing purposes.
    27. this.locations = new ArrayList<SerializableLocation>();
    28. SerializableLocation sLocationOne = new SerializableLocation(Bukkit
    29. .getWorlds().get(0), 1, 2, 3, 4, 5);
    30. SerializableLocation sLocationTwo = new SerializableLocation(Bukkit
    31. .getWorlds().get(0), 6, 7, 8, 9, 10);
    32. locations.add(sLocationOne);
    33. locations.add(sLocationTwo);
    34. try
    35. {
    36. // Again you have to have ConfigurationSerialization.registerClass()
    37. // done before being able to use the line below. You also do not
    38. // need to have a list of serializable locations either and can just
    39. // use one location and to retrieve use the
    40. // saveConfig.get(pathForObject).
    41. saveConfig.set(serializedLocationsPath, locations);
    42. saveConfig.save(saveFile);
    43. }
    44. catch (Exception ex)
    45. {
    46. logger.severe("Unable to save locations to yml format!");
    47. ex.printStackTrace();
    48. }
    49. locations = null;
    50. saveConfig = null;
    51. saveFile = null;
    52. logger = null;
    53. }
    54.  
    55. @Override
    56. public void onLoad()
    57. {
    58. // Get our plugin's logger
    59. this.logger = getLogger();
    60.  
    61. // All of this can be done in the onEnable method as well
    62. // Tells Bukkit that we have a custom class we made that can handle
    63. // loading/saving from Map<String, Object> into a FileConfigurations
    64. ConfigurationSerialization.registerClass(SerializableLocation.class);
    65.  
    66. // Create the save file. I use getAbsolutePath() just in case the
    67. // directories leading to my file are not created
    68. this.saveFile = new File(getDataFolder().getAbsolutePath(),
    69. "serialized-locations.yml");
    70.  
    71. // Load the saveFile into our saveConfig
    72. this.saveConfig = YamlConfiguration.loadConfiguration(saveFile);
    73.  
    74. // Check to see if the section we are about to load exists
    75. if (saveConfig.isList(serializedLocationsPath))
    76. {
    77. // We would not be able to use the saveConfig.getList() and cast to
    78. // our custom object if we did not register is first with
    79. // ConfigurationSerialization.registerClass()
    80. @SuppressWarnings("unchecked")
    81. List<SerializableLocation> serializedLocations = (List<SerializableLocation>) saveConfig
    82. .getList(serializedLocationsPath);
    83. for (SerializableLocation serializedLocation : serializedLocations)
    84. {
    85. printLocation(serializedLocation.getLocation());
    86. }
    87. }
    88. else
    89. {
    90. logger.info("No serialized locations were found to load!");
    91. }
    92. }
    93.  
    94. private void printLocation(Location location)
    95. {
    96. World world = location.getWorld();
    97. double x = location.getX();
    98. double y = location.getY();
    99. double z = location.getZ();
    100. float yaw = location.getYaw();
    101. float pitch = location.getPitch();
    102. logger.info("World: " + world.getName() + " X: " + x + " Y: " + y
    103. + " Z: " + z + " Yaw: " + yaw + " Pitch: " + pitch);
    104. }
    105. }

    SerializableLocation.java
    Code:java
    1. import org.bukkit.Bukkit;
    2. import org.bukkit.Location;
    3. import org.bukkit.World;
    4. import org.bukkit.configuration.serialization.ConfigurationSerializable;
    5.  
    6. public class SerializableLocation implements ConfigurationSerializable
    7. {
    8. private String worldName;
    9. private double x, y, z;
    10. private float yaw, pitch;
    11.  
    12. public SerializableLocation(World world, double x, double y, double z,
    13. float yaw, float pitch)
    14. {
    15. this.worldName = world.getName();
    16. this.x = x;
    17. this.y = y;
    18. this.z = z;
    19. this.yaw = yaw;
    20. this.pitch = pitch;
    21. }
    22.  
    23. public SerializableLocation(Location location)
    24. {
    25. this(location.getWorld(), location.getX(), location.getY(), location
    26. .getZ(), location.getYaw(), location.getPitch());
    27. }
    28.  
    29. public SerializableLocation(Map<String, Object> map)
    30. {
    31. this(Bukkit.getWorld((String) map.get("world-name")), Double
    32. .parseDouble((String) map.get("x")), Double
    33. .parseDouble((String) map.get("y")), Double
    34. .parseDouble((String) map.get("z")), Float
    35. .parseFloat((String) map.get("yaw")), Float
    36. .parseFloat((String) map.get("pitch")));
    37. }
    38.  
    39. public Location getLocation()
    40. {
    41. return new Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch);
    42. }
    43.  
    44. @Override
    45. public Map<String, Object> serialize()
    46. {
    47. HashMap<String, Object> map = new HashMap<String, Object>();
    48. map.put("world-name", worldName);
    49. map.put("x", x);
    50. map.put("y", y);
    51. map.put("z", z);
    52. map.put("yaw", yaw);
    53. map.put("pitch", pitch);
    54. return map;
    55. }
    56.  
    57. }
    58.  
     
  5. Offline

    gyroninja

    Hi Dragonphase I would recommend you write the location to the config in one call. Right now you are using 4 calls per location. Writing or reading to disk is an expensive call. Even though I think bukkit loads the whole configuration map at once. It is good practice when serializing data to use a string builder that appends the data together with delimiters inbetween. I also would recommend using the configuration serialization library. You can look into it on github under the package org.bukkit.configuration.serialization.
     
  6. Offline

    MiniDigger

    gyroninja I don't get your comment. I don't see where Dragonphase write anything to the disk. He is just putting things in the Map, which is stored in the memory. The Map only gets written on the disk when you save the config.
    Also this this ressource is all about ConfigurationSerialization so there in no need to recommand that part of the bukkit api. Please read the ressource bevor posting an comment!
     
  7. Offline

    desht

    Dragonphase nice, but I'd also recommend implementing hashCode() and equals() (and toString() might be nice too).

    Implementing hashCode() and equals() in particular will allow your objects to a) be compared with .equals() and b) used as keys in a HashMap. Pretty easy to add; your IDE almost certainly has a menu entry to create the two methods (e.g. Intellij has Code -> Generate... -> equals() and hashCode()).

    Also, storing the World's UUID rather than its name is (slightly) safer, especially if the object is being persisted - a world's name could change, but its UUID won't.

    (And since posting serializable Location classes seems to be fashionable, here's one I wrote: https://github.com/desht/dhutils/bl...ava/me/desht/dhutils/PersistableLocation.java)
     
    TigerHix and Dragonphase like this.
  8. Offline

    Dragonphase

    gyroninja
    I understand where you are coming from, however, this post is more designed to demonstrate the use of Bukkit's Serialization API, and it is something that people should take advantage of given the opportunity. When serializing the data to disk, it is stored in a HashMap and written and retrieved as such. There's no need to split strings.

    desht
    This is pretty useful information. I have implemented hashCode and equals to allow objects to be compared on previous occasions. You also have a really valid point on storing the UUID rather than the name :)
     
  9. Offline

    raGan.


    This actually depends on the purpose of the saved locaiton. Client could have per-world config file with locations (for example world border corner points) and need to copy it to a different server with the same world name, and it could work with String worlds, but break with UUIDs. I'm not sure if that's the best example, but hopefully sufficient. I personally prefer world in String form when serializing locations, because it is more user friendly even though it might not be as safe.
     
    desht and Dragonphase like this.
  10. Offline

    Dragonphase

    raGan.

    That's true. I guess it comes down to how you prioritize it.
     
Thread Status:
Not open for further replies.

Share This Page