Solved Persistent Custom Maps

Discussion in 'Plugin Development' started by NathanWolf, Dec 12, 2013.

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

    NathanWolf

    FINAL EDIT: See the last post for a solution if you're interested in showing player portraits (or any image) on a map in your plugin.

    I've recently gotten into the Map API (I love it!), but there's one thing I'm not understanding- how do the renderers stay "attached" to their map views across server reloads? (Or... do they?)

    EDIT: I think I'm realizing they do not, and it's up to me to keep track of my custom map id's and reattach the renderers... does that sound right? If so, please still read below because the 1.6 client crash seems weird to me.

    I'm finding that after a server reload, my custom maps reset to a default map:

    Before:
    [​IMG]

    After Server Reload:
    [​IMG]

    The same applies to map items in my inventory (not a frame).

    Furthermore, I've noticed if I switch to 1.6 CB and the 1.6 client, my custom map actually crashes the client when viewed (!)

    What am I doing wrong? :)

    My renderer:
    https://raw.github.com/elBukkit/Mag...akers/mine/bukkit/utilities/SkinRenderer.java

    The render method (which I imagine is the important bit):

    Code:java
    1. @Override
    2. public void render(MapView map, MapCanvas canvas, Player player) {
    3. if (portraitImage == null) {
    4. loadSkin();
    5. }
    6. if (portraitImage != null) {
    7. canvas.drawImage(0, 0, portraitImage);
    8. canvas.drawText(2, 116, MinecraftFont.Font, "¤" + MapPalette.GRAY_2 + ";" + playerName);
    9. canvas.drawText(3, 117, MinecraftFont.Font, "¤" + MapPalette.WHITE + ";" + playerName);
    10. }
    11. }


    I create the map like this:

    https://raw.github.com/elBukkit/Mag.../bukkit/plugins/magic/spells/CameraSpell.java

    Code:java
    1. MapView newMap = Bukkit.createMap(world);
    2. for(MapRenderer renderer : newMap.getRenderers()) {
    3. newMap.removeRenderer(renderer);
    4. }
    5. MapRenderer renderer = new SkinRenderer(targetPlayer.getName());
    6. newMap.addRenderer(renderer);
    7. ItemStack newMapItem = new ItemStack(Material.MAP, 1, newMap.getId());
    8. world.dropItemNaturally(player.getLocation(), newMapItem);


    Thanks for any help/advice! I assume there's a way to make a map persistently hold an image, else people wouldn't be using these for signs and such... right?
     
  2. Offline

    NathanWolf

    In case anyone was curious, I followed some advice from desht in a related thread, and got this working. If anyone is really curious, here's my renderer:

    https://raw.github.com/elBukkit/Mag...akers/mine/bukkit/utilities/SkinRenderer.java

    In order to use this, you have to persist a map of player names to ids, and call SkinRenderer. loadPlayers() in your plugin's onEnable. (Well, I actually find it works better if you delay it a few seconds).

    Then when you want to "take a picture" of another player, you would do something like this:

    Code:java
    1. @SuppressWarnings("deprecation")
    2. public static MapView getPlayerPortrait(String playerName) {
    3. // We're going to always take maps from the main world
    4. // This is to avoid potential problems with maps created in other worlds...
    5. // But it all has the potential to get out of sync anyway.
    6. World world = Bukkit.getWorlds().get(0);
    7.  
    8. MapView playerMap = null;
    9. if (playerPortraitIds.containsKey(playerName)) {
    10. playerMap = Bukkit.getMap(playerPortraitIds.get(playerName));
    11. }
    12.  
    13. if (playerMap == null) {
    14. // Remove old entry ... since apparently this map is broken?
    15. if (playerPortraitIds.containsKey(playerName)) {
    16. Short id = playerPortraitIds.get(playerName);
    17. plugin.getLogger().info("Unregistering map id " + id + " from " + playerName + ", map failed to load");
    18. portraitIdPlayers.remove(id);
    19. playerPortraitIds.remove(playerName);
    20. }
    21. playerMap = Bukkit.createMap(world);
    22. if (playerMap != null) {
    23. plugin.getLogger().info("Created new map id " + playerMap.getId() + " for " + playerName);
    24. playerPortraitIds.put(playerName, playerMap.getId());
    25. portraitIdPlayers.put(playerMap.getId(), playerName);
    26. }
    27. }
    28.  
    29. if (playerMap == null) return null;
    30.  
    31. for (MapRenderer renderer : playerMap.getRenderers()) {
    32. playerMap.removeRenderer(renderer);
    33. }
    34. MapRenderer renderer = new SkinRenderer(playerName);
    35. playerMap.addRenderer(renderer);
    36.  
    37. return playerMap;
    38. }


    I could've made the code a little more encapsulated (as in handling its own map id persistence), but for my plugin I wanted to integrate it into a general player data persistence system, so that's why I left it separate.

    The 1.6 crash I got I think was unrelated- I believe I got my world data in such a state that there was a map entity in a frame that had an id which had not yet been generated by the world I was in. 1.7 did seem to handle it more gracefully, though.
     
  3. Offline

    NathanWolf

    Ok, one more bump for this, then I'm done. In case anyone ever comes looking (though I have to say, this thread seems invisible- I couldn't find it except in my "watched" list).

    Anyway, if you want to add functionality to your plugin to display images (or player portraits) on maps, this class may be for you:

    https://raw.github.com/elBukkit/Mag...om/elmakers/mine/bukkit/utilities/URLMap.java

    I made it completely self-contained. Use it like so:

    Code:java
    1. // In your plugin's onEnable
    2. public void onEnable {
    3. // This needs to be delayed a little bit, for some reason.
    4. Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
    5. public void run() {
    6. // This resets all renderers to their default state
    7. URLMap.resetAll();
    8. // This loads the configuration file and initializes saved renderers
    9. URLMap.load(plugin);
    10. }
    11. }, 20);
    12. ...
    13. }
    14.  
    15. // Anywhere else in your code:
    16.  
    17. // To Create a map with a URL
    18. ItemStack mapitem = URLMap.getURLItem(url, x, y, width, height, "Cropped Photo");
    19. // Or just
    20. ItemStack fullSize = URLMap.getURLItem(url, "Photo");
    21. // Helper function for player portraits
    22. ItemStack playerPortrait = URLMap.getPlayerPortrait(playerName);


    Maps id's are stored in a yml file in your plugin's folder, this file is written to as new maps are created.

    Images are loaded asynchronously, and I've taken great care to make sure I don't lag the server (let me know if you experience otherwise).

    Hope this helps somebody!
     
Thread Status:
Not open for further replies.

Share This Page