Solved Question about MapRenderers

Discussion in 'Plugin Development' started by DarkBladee12, Jan 24, 2013.

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

    DarkBladee12

    Hey guys, I'm currently playing around a bit with the CraftMapRenderer and managed to print pictures on maps. Everything works fine, but if I restart the server and log in the maps turn to normal maps! How can I prevent that or isn't this possible?
     
  2. Offline

    desht

    At what point do you add your map renderer to the map? Renderers are not persistent - you need to add them when your plugin enables (or when you get a MapInitializeEvent with the ID of the map you're taking control of).
     
  3. Offline

    DarkBladee12

    desht so I have to save the ID's and check the MapInitializeEvent? I make a new map and add the map renderer, after that I add it to the player's inventory. Here's my code:
    Code:
    public ItemStack getLogo(){
            ItemStack m = new ItemStack(Material.MAP);
            MapView map = Bukkit.createMap(Bukkit.getWorlds().get(0));
            for (MapRenderer mr : map.getRenderers()) {
                map.removeRenderer(mr);
            }
            map.addRenderer(new LogoRenderer(map, new WorldMap(Bukkit.getWorlds().get(0).getName())));
            m.setDurability(map.getId());
            return m;
        }
     
  4. Offline

    desht

    Calling Bukkit.createMap() each time will create another new map, and you'll eventually run out of map IDs. So don't do that. If you look in your bukkit/world/data directory, you'll likely have a lot of map_X.dat files, one for every time that getLogo() method ran. It also explains why maps that worked before server restart no longer work - you've added your renderer on a completely different map ID.

    Your plugin will need to keep track of a map ID, which doesn't change after first allocation. You could store the ID in your plugin config.yml, but it's not something you want your plugin users to be changing. So maybe consider storing it elsewhere - your choice. Then do something like this:

    Code:
    public MapView setupMapView() {
      int id = getMapId();
      MapView mv = Bukkit.getMap(id);
      if (mv == null) {
        mv = Bukkit.createMap(Bukkit.getWorlds().get(0));
        storeMapId(mv.getId());
      }
      // now remove vanilla renderer & add your own
    }
    
    Have your getMapId() method return -1 (an invalid map ID) if one hasn't been set up yet, i.e. the plugin hasn't run on this server before.

    You could run this from your onEnable() - I do this for saved map views in ScrollingMenuSign, and it works nicely (here I keep track of multiple map views, one for each view - the map ID is saved in the persisted view data). As for the MapInitializeEvent - I'm not honestly not sure where it's most appropriate to listen for that. Setting up the map renderer at plugin enable time works fine.
     
    NathanWolf likes this.
  5. Offline

    DarkBladee12

    desht I managed to get this methods now:

    Code:
    public ItemStack getLogo() {
            Configuration config = plugin.getConfig();
            List<Integer> mlist = config.getIntegerList("Maps");
            ItemStack m = new ItemStack(Material.MAP);
            MapView map = Bukkit.createMap(Bukkit.getWorlds().get(0));
            mlist.add((int) map.getId());
            config.set("Maps", mlist);
            plugin.saveConfig();
            map.getRenderers().clear();
            map.addRenderer(new LogoRenderer(map, new WorldMap(Bukkit.getWorlds().get(0).getName())));
            m.setDurability(map.getId());
            return m;
        }
     
        public void setupMaps() {
            Configuration config = plugin.getConfig();
            if (config.getIntegerList("Maps").size() == 0) {
                return;
            }
            List<Integer> mlist = config.getIntegerList("Maps");
            List<Integer> nmlist = new ArrayList<Integer>();
            for (int id : mlist) {
                MapView mv = Bukkit.getMap((short) id);
                if (mv == null) {
                    mv = Bukkit.createMap(Bukkit.getWorlds().get(0));
                }
                mv.getRenderers().clear();
                mv.addRenderer(new LogoRenderer(mv, new WorldMap(Bukkit.getWorlds().get(0).getName())));
                nmlist.add((int) mv.getId());
            }
            config.set("Maps", nmlist);
            plugin.saveConfig();
        }
    Is that code right now? I've noticed that the server gets very laggy with those renderers, how can I prevent that?

    Anyone knows why the renderer causes massive laggs?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 31, 2016
  6. Offline

    desht

    Neither of those methods are renderers. They're both just methods to set up renderers, and they seem to be doing sort of the same task - you should only need a single method to do it, which you can run from onEnable(). Just follow the pseudo code I posted before and you should be OK.

    Your actual renderer is the render() method in your LogoRenderer class, which you haven't posted. You need to be as efficient as possible in that method, and not do any unnecessary calculations there.
     
  7. Offline

    DarkBladee12

    desht this is my complete code now:

    Code:
    public class PictureRenderer extends CraftMapRenderer {
        private String file;
     
        public PictureRenderer(MapView view, WorldMap w, String file) {
            super((CraftMapView) view, w);
            this.file = file;
        }
     
        @Override
        public void render(MapView mv, MapCanvas mc, Player p) {
            File file = new File("plugins/Pixelator/" + this.file);
            BufferedImage img = null;
            try {
                img = ImageIO.read(file);
            } catch (IOException e) {
                try {
                    mc.drawImage(0, 0, ImageIO.read(getClass().getResourceAsStream("error.png")));
                } catch (IOException e1) {
                    mv.getRenderers().clear();
                    return;
                }
                for (MapRenderer mr : mv.getRenderers()) {
                    mv.removeRenderer(mr);
                }
                return;
            }
            mc.drawImage(0, 0, img);
        }
    }
    Code:
    public ItemStack getPicture(String file) {
            Configuration config = plugin.getConfig();
            List<String> mlist = config.getStringList("Maps");
            ItemStack m = new ItemStack(Material.MAP);
            MapView map = Bukkit.createMap(Bukkit.getWorlds().get(0));
            mlist.add((int) map.getId() + ", " + file);
            config.set("Maps", mlist);
            plugin.saveConfig();
            for (MapRenderer mr : map.getRenderers()) {
                map.removeRenderer(mr);
            }
            map.addRenderer(new PictureRenderer(map, new WorldMap(String.valueOf(map.getId())), file));
            ItemMeta im = m.getItemMeta();
            im.setDisplayName("§2§o" + file);
            m.setItemMeta(im);
            m.setDurability(map.getId());
            return m;
        }
    Code:
    public void setupMaps() {
            Configuration config = plugin.getConfig();
            if (config.getStringList("Maps").size() == 0) {
                return;
            }
            List<String> mlist = config.getStringList("Maps");
            List<String> nmlist = new ArrayList<String>();
            for (String ids : mlist) {
                String[] split = ids.split(", ");
                int id = Integer.parseInt(split[0]);
                String file = split[1];
                MapView mv = Bukkit.getMap((short) id);
                if (mv == null) {
                    mv = Bukkit.createMap(Bukkit.getWorlds().get(0));
                }
                for (MapRenderer mr : mv.getRenderers()) {
                    mv.removeRenderer(mr);
                }
                mv.addRenderer(new PictureRenderer(mv, new WorldMap(String.valueOf(mv.getId())), file));
                nmlist.add((int) mv.getId() + ", " + file);
            }
            config.set("Maps", nmlist);
            plugin.saveConfig();
        }
     
  8. Offline

    desht

    Yeah that'll kill your performance for sure. render() gets called every tick for every player with one of your maps in their hand, and you're reading an image from file and copying into the map canvas every single time render() is called.

    You only need to draw onto your canvas when the data you're rendering changes. Since the data in your case is a static image, that means a single call to render() is all you need. Have a boolean field in your renderer class, and use it to control whether or not the image gets drawn (set it after you've drawn the image the first time, and check it before you do any drawing, so subsequent calls to render() return immediately).
     
    DarkBladee12 likes this.
  9. Offline

    DarkBladee12

    desht Thanks I'll try that ;)
     
  10. On a side note, even if the rendered data would be dynamic but based off of an image file, you may never keep reading in the file from hard disk every time you access it! You would be constantly opening the file, reading its contents, using the contents to build the image data in memory, and only then copy it over to the rendering canvas.
    All of these steps except for the latter only need to be done once!
    So you would read the file using ImageIO.read once in the constructor and store it as an attribute, and then just access that BufferedImage from the render method.
    That's the real performance killer in your first approach. Rendering an image from memory 20 times a second (or 40, 60, whatever ...) shouldn't even be a problem (which doesn't mean that it isn't unnecessary, as desht pointed out already ;)).
     
    DarkBladee12 likes this.
  11. Offline

    DarkBladee12

    Bone008 Thank you too, now it works fine ;)
     
  12. Offline

    _Belknap_

    DarkBladee12
    Hey DarkBladee if your interested I am creating a Wizards plugin based off of Wizard101 and after looking at this thread and then Pixelator, I would love your help on creating the physical "cards" part which will be maps with pictures of the cards on them. I will add you as an author to the plugin page on CraftBukkit when it is finished if you're up for it! My email is [email protected].
    Thanks!
     
Thread Status:
Not open for further replies.

Share This Page