Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

Loading sprites from URL on separate thread

Help
2011-12-01
2013-06-06
  • Zlatan Momic
    Zlatan Momic
    2011-12-01

    I have tried to load sprites on separate thread. So when ImageIO tries to read sprite from URL, client would not hang for a moment.

    Idea is to load and return failsafe sprite first and after background thread finishes download it adds fresh sprite to cache.

                    public Sprite getSprite(final String ref) {
                        final SpriteCache cache = SpriteCache.get();
                        // try to get cashed sprite
                        Sprite sprite = cache.get(ref);
                        // if there is no cashed sprite 
                        if (sprite == null) {
                            // load failsafe while waiting for sprite to be loaded from URL
                            sprite = loadSprite(FAILSAFE_ICON_REF);
                            Thread thread = new Thread() {
                                @Override
                                public void run() {
                                    try {
                                        Thread.sleep(200);
                                    } catch (InterruptedException ex) {}
                                    // load and add sprite to cache
                                    Sprite sprite = loadSprite(ref);
                                    if (sprite != null) {
                                        cache.add(ref, sprite);
                                    }
                                }
                            };
                            thread.start();
                        }
                        return sprite;
                    }
    

    Thread is executing properly but changes to cache are not visible. It's like entities and gui components needs to be refreshed from cache.

    What am I missing here?
    If someone have any idea how to do this better, please help.
    Thx

     
  • Kimmo Rundelin
    Kimmo Rundelin
    2011-12-01

    Pretty much everything (entities, maps, etc) loads their sprites only once, so it never gets refreshed from the cache. One possibility could be handling it in the ImageSprite by changing the internal image reference in the background thread. This would still be a problem for derived sprites, so ImageSprite should still probably block drawing until the correct image has been loaded. So it uncertain if that would have any positive effect (the client already tries to load most sprites in the game loop thread, which spends most of its time sleeping).

     
  • Zlatan Momic
    Zlatan Momic
    2011-12-05

    Well, that did it. Changing reference will initiate IEntity.PROP_CLASS change and representation change of entity?

    After background thread finishes download and after Entity's reference has changed, should I add it to cache again? Looks like client works the same regardless of adding it to cache again.

    Here is the code. If you have any suggestion please comment it…

    SpriteStore.java method loadSprite(final String ref)

        if (ref.startsWith("http://")) {
            logger.info("Loading sprite from a URL...");
            final ImageSprite sprite = new ImageSprite(getFailsafe(), FAILSAFE_ICON_REF, ref);
            sprite.loadInBackground();
            return sprite;
        }
    

    ImageSprite.java

        public ImageSprite(final Sprite sprite, final String reference, final String newReference) {
            this(sprite, reference);
            this.newReference = newReference;
        }
        public void loadInBackground() {
            if (newReference == null) 
                return;
            
            Thread t = new Thread(downloader);
            t.start();
        }
        
        private final Runnable downloader = new Runnable() {
            public void run() {
                BufferedImage sourceImage = null;
                try {
                    URL url = new URL(newReference.toString());
                    sourceImage = ImageIO.read(url);
                } catch (IOException ex) {
                    logger.error("Loading sprite from URL failed: " + newReference, ex);
                    return;
                }
                if (sourceImage == null) {
                    logger.error("Loading sprite from URL failed: " + newReference);
                    return;
                }
                // update sprite image and reference
                ImageSprite.this.image = sourceImage;
                ImageSprite.this.reference = newReference;
                // adding it to cache, DO I NEED THIS???
                final SpriteCache cache = SpriteCache.get();
                cache.add(newReference, ImageSprite.this);
            }
        };
    
     
  • Kimmo Rundelin
    Kimmo Rundelin
    2011-12-07

    EntityViews do not get any notice, but the next time the sprite is drawn the new image is used. For purposes where the size of the sprite matters, the sprite will likely need an appropriate notifier interface.

    Caching the sprite again is not necessary, as the sprite reference has not changed, just its internal state.

    I'd also consider using executors, like kymara suggested (ThreadPoolExecutor, probably), to have a sane amount of download threads in case many images need to be downloaded simultaneously.

     


Anonymous


Cancel   Add attachments