Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (513)
Games in Android Showcase (120)
games submitted by our members
Games in WIP (577)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Generics, interfaces and awkward dependancies  (Read 1309 times)
0 Members and 1 Guest are viewing this topic.
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Posted 2011-12-30 19:29:36 »


I have a bit of a quandary in terms of dependencies and interfaces.

I have Rebirth ( http://triangularpixels.net/games/tools/rebirth/ ) which handles all game resources (images, sounds, etc. ). Resources all implement the Resource interface:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
public interface Resource
{
   public void setName(String name);
   
   public String getName();
   
   public void setIO(ResourceIO io);
   public void setFileMonitor(FileMonitor fileMonitor);
   
   public void destroy();

   public void parse(Element element, List<Resource> outChildResources);
   
   public void create(ResourceHandleList resources);
   
   public boolean mainCreate();
}


It’s a bit wider than it needs to be, but that can be changed. One of the main things Rebirth does is hot-reloading when files change on disk. To do that resources are accessed via a ResourceHandle, eg:

1  
2  
3  
4  
5  
6  
// Find resource (init time)
ResourceHandle<DiskImageResource> icon = resources.get(“icon.image”);

// Usage - get actual image and use to draw
DiskImageResource actualIcon = icon.get();
screen.draw(actualIcon, x, y);


So you might end up with DiskImageResource which looks something like:

1  
2  
3  
4  
5  
6  
7  
8  
9  
class DiskImageResource implements Resource
{
  // .. stuff ..

  public void create(ResourceHandleList resources)
  {
    this.image = ImageIO.load(imagePath);
  }
}


This works great until I have to start passing resources into other sub systems. For example I want my Screen to have something like:

1  
public void draw(IImage image);


where IImage is just a bare-bones image with rgb data and width/height.

The problem: I can’t have a Resource<IImage> since ResourceHandle is defined as ResourceHandle<T extends Resource>.

Possible solutions:

1. Make IImage extend Resource. Works, but now I’ve got resource dependancies in my graphics subsystem (or whatever). Also not great because really IImage *isn’t* a Resource, it’s just doing that to play nice with another part of the code.

2. Change to ResourceHandle<T> so resource handle can hold any object. Removes the awkward dependency, but means I have to do odd casts and/or instanceof inside the resource manager and possibly loose some type safety.

3. Kill off ResourceHandle completely and instead do scary voodoo with java.lang.Proxy. Doesn’t necessarily solve the problem of having a root Resource interface though.

4. Something else I’ve not thought off.

It’s the kind of thing that really needs proper python-style duck typing to work I guess. I don’t mind other subsystems being dependant on ResourceHandle because that’s a *tiny* concrete class (it really just has get() and that’s it), but I don’t like forcing everything else to use an external base class.

Thoughts?

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline lhkbob

JGO Knight


Medals: 32



« Reply #1 - Posted 2011-12-30 21:09:53 »

I'm having a little trouble wrapping my head around your interfaces and their responsibilities, so maybe I'm just confused, but why do you need a Resource<IImage>?

Why not just have an ImageResource interface that adds a getIImage() method.  Then your DiskImageResource can extend that and expose the IImage data.  Your graphics subsystem wouldn't need to know about the resource representation, you'd just make sure to grab the IImage data from the resource when calling the graphics subsystem from the main part of your engine.

In your example, the draw() method does need to worry about hot-swapping and reloading since it's a per-frame application.  I'm assuming it gets trickier when you have some entity that needs to keep track of which images it needs to be drawn with for its appearance, but that's one layer above the graphics system and it makes more sense to allow it to depend on resources.

Part of my confusion with your API at the moment is the difference between a Resource and a ResourceHandle.  Which is responsible for determining when hot-swapping/reloading must occur?  It already appears as though Resource does some of this because they have setters for an IO and a FileMonitor.

Offline Don Kiddick

Junior Duke





« Reply #2 - Posted 2011-12-31 00:10:13 »

Rather than having IImage implement Resource, you could use composition instead.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
class ImageResource implements Resource {

  private IImage image;

  public IImage getImage() { return image; }

}

then you can write,

ResourceHandle<ImageResource> handle = resources.get(“icon.image”);
ImageResource imageResource = handle.get();
IImage image = imageResource.getImage();
screen.draw(image);


D.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Don Kiddick

Junior Duke





« Reply #3 - Posted 2011-12-31 00:11:26 »

I would also add that IMHO beyond trivial collection uses, generics introduce more complication than they remove.
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #4 - Posted 2011-12-31 01:30:36 »

I'm having a little trouble wrapping my head around your interfaces and their responsibilities, so maybe I'm just confused, but why do you need a Resource<IImage>?

Part of my confusion with your API at the moment is the difference between a Resource and a ResourceHandle.  Which is responsible for determining when hot-swapping/reloading must occur?  It already appears as though Resource does some of this because they have setters for an IO and a FileMonitor.

Ok, so quick overview:

'resources' are any static data that is loaded at runtime. Sounds, images, enemy movement speeds, etc. This is done by implementing a base Resource interface and doing whatever custom loading/parsing is required to load the raw data. The rebirth resource manager handles the generic low-level stuff like turning a series of xml files into calls to your resource c'tors and other methods.

ResourceHandle is a concrete type that's part of the core resource manager, and holds a reference to the actual resource it's managing, as well as a bit of book-keeping info (like what load state it's in). Most importantly, it means the gameplay code can hold onto a ResourceHandle<Foo> instead of a Foo directly. This means that when the resource manager detects a modified file it can re-parse the resource files and create a new Foo, then swap it out for the previous version of the Foo object.

The gameplay code doesn't care that in frame 10 it's got Foo instance 1, and in frame 11 it's got Foo instance 2, since it calls fooHandle.get() before using it each time.

90% of the time the resource manager can hot-reload resources in a generic way without the resource itself having to be coded specially. For the remaining 10% (ones that depend on non-trivial file parsing) they can use the resource IO and FileMonitor to hot-reload without making a new instance.

If you're really interested, the docs and tutorial might be worth a read: http://triangularpixels.net/games/tools/rebirth/

Quote from: Don Kiddick
Rather than having IImage implement Resource, you could use composition instead.

This only really shifts the dependancy down a layer. It works in some circumstances (and I've already been using it) but not when I want to pass a resource handle into a subsystem itself (as I still have the dependancy on Resource somewhere in the chain).

Quote
I would also add that IMHO beyond trivial collection uses, generics introduce more complication than they remove.
This definitely seems the case. However it does grant additional type safety that I'd rather not throw away, and if I can hide the complexity in the internals of the resource manager that'd be ok.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #5 - Posted 2011-12-31 01:44:35 »

On further thought, it's the requirement to hot-swap actual resource instances that causes much of the pain. Which leads me back to option 3 - kill off ResourceHandle<> and just deal with objects which implement Resource. That makes the subsystem dependancy problem go away (since DiskImage could implement both Resource and IImage).

But the problem then become elegantly handling hot-reloading. Currently a (say) DiskImage class has a very simple life-cycle: c'tor, parse(), create(), mainCreate() and then destroy(). Always in that order, always a maximum of once each (the only alternate lifecycle is c'tor, parse(), destroy()).

If I change things to hot-reload without actually swapping instances then the resource life-cycle would have to allow calling parse(), create(), mainCreate() and destroy() multiple times. With all the extra book-keeping of keeping track of state internally, and the problems of left-over state needing to be reset correctly. Since one of the main goals was to make writing a new resource type as simple as possible and for hot-reloading to 'just work' that would seem to be a big step backwards...

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline lhkbob

JGO Knight


Medals: 32



« Reply #6 - Posted 2011-12-31 07:41:51 »

Thanks for the further explanation.  I'll write out some of my brain-storming to see if we're on the same page.

My first thought was to have DiskImageResource implement both Resource and IImage.
1  
class DiskImageResource implements Resource, IImage { }

Then you can have a ResourceHandle<DiskImageResource> and pass the result of get() into the graphics draw() method. However, this doesn't extend will if you have multiple types of image resources that all need to be converted to IImage and you want to handle them generically, since (as you said before) you can't have a ResourceHandle<? extends IImage>.

Using composition does not seem like a terrible idea, but you've already found the case where that doesn't work.

Striving for a strict lifecycle on resources is a good goal.  I think it could help in multithreading situations and you don't have to worry about if something's state changes half-way in a frame.

I think the problem here is that you have a subsystem that needs access to the ResourceHandle.  Unless there is more going on than I think, it's unlikely that it needs the actual ResourceHandle and cares that it is a DiskImageResource but that it is capable of providing an IImage.  Would it be possible to have another interface that is even less specific than ResourceHandle, for lack of a better name, such as Provider<T>?

Your subsystems could ask for a Provider<IImage> and you can pass in a very simple implementation that wraps the ResourceHandle.  That way the subsystem can take advantage of the resource hot-swapping without knowing anything about the resource API.

Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #7 - Posted 2011-12-31 12:23:37 »

I think the problem here is that you have a subsystem that needs access to the ResourceHandle.  Unless there is more going on than I think, it's unlikely that it needs the actual ResourceHandle and cares that it is a DiskImageResource but that it is capable of providing an IImage.  Would it be possible to have another interface that is even less specific than ResourceHandle, for lack of a better name, such as Provider<T>?
Interesting! Yes that does sound like it would work. Something like:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
interface Provider<T>
{
  public T get();
}

class ResourceHandle<T extends Resource> implements Provider<T>
{
  // same as before
}

// custom resource
class DiskImageResource implements Resource, IImage
{
 // ..stuff..
}

// Then inside graphics/whatever
class Screen
{
  public void draw(Provider<IImage> image)
  {
    IImage img = image.get();
    // ..stuff..
  }
}

// usage
ResourceHandle<DiskImageResource> img = resources.get("foo.image");
screen.draw(img);


I think this would work, unless something kooky in generics prevents this kind of usage. I shall have to try it.

Even better, Provider<T> could be replaced with java.lang.ref.Reference<T>, which means that both the resource system and the graphics system only depend on the core jre classes, not a common Provider class. However javadocs say you can't subclass Reference (and I haven't tried) so that doesn't seem to be possible. Maybe I could (ab)use WeakReference or AtomicReference instead? Although that would be going against the spirit of those classes I think. Or are there other alternatives?

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline lhkbob

JGO Knight


Medals: 32



« Reply #8 - Posted 2011-12-31 19:15:39 »

I know you are able to subclass WeakReference and SoftReference, but that feels like you'd be abusing them.

I don't see anything wrong with defining the type in your core library, although you could decide to pull in the JSR for dependency injection, or Guava, both of which define provider or supplier like interfaces.  Since you only need the one type, though, pulling in a whole dependency feels like overkill to me.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Longarmx (52 views)
2014-10-17 03:59:02

Norakomi (44 views)
2014-10-16 15:22:06

Norakomi (34 views)
2014-10-16 15:20:20

lcass (38 views)
2014-10-15 16:18:58

TehJavaDev (68 views)
2014-10-14 00:39:48

TehJavaDev (68 views)
2014-10-14 00:35:47

TehJavaDev (60 views)
2014-10-14 00:32:37

BurntPizza (73 views)
2014-10-11 23:24:42

BurntPizza (45 views)
2014-10-11 23:10:45

BurntPizza (86 views)
2014-10-11 22:30:10
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!