Games development

Managing groups of Assets

0

Games are resource intensive applications in terms of both memory and CPU time. Offtimes it’s necessary to render hundreds of different sprites on screen whilst playing and manipulating dozens of sound effects samples. Logically, those assets need to be stored in memory and such precious elixir doesn’t come for free.

Fair enough, you can probably afford to brainlessly waste some memory on PC when working on small projects but in the world of mobile devices is just the complete opposite. Not only phones and tablets have limited memory sizes, but theirs is also shared by many hungry apps and fancy background processes. If your app’s demands causes everything else to shut down, you’re going to make your users sad.

The sooner you face this, the better. Asset management can be a pain in the arse.

Asset management? Fuck this shit!

Asset management? Fuck this shit!

Throughout this article, I’d like to illustrate some of the challenges one has to face when dealing with many assets within a medium sized project. Later on, I’ll be presenting some classic approaches to finish with one that seems to be working for me quite well. The examples are focused on the libgdx framework, although the concepts are widely applicable.

The very basics

Just to make sure we’re on the same page, these two are the very fundamental items to take into account when managing resources in a game.

  • Don’t load things twice. Eg. If you have identical looking sprites, share the texture data and render it as needed.
  • Don’t load anything that you won’t need in the near future. Why would you load the final level when the user has just fired up the game for the very first time?

Basically, we’re going to try to use the smallest portion of memory we possibly can at a given point in execution.

Use the AssetManager Luke

Luckily enough, libgdx provides the splendid AssetManager, so, for $deity’s sake, please don’t reinvent the wheel. Are you not using libgdx? Fear not, fellow developer, for most modern libraries feature similar components. Eg. In Ogre3D we have ResourceGroupManager class.

Take all the time you need to read the correspondent wiki page in case you were oblivious to its existence. Long story short, here’s what AssetManager can do for you.

  • Store one copy of each asset.
  • Reference counting.
  • Inter asset dependencies handling.
  • Asynchronous asset loading, keeps your app responsive.
  • Easily extensible, implement your custom loaders for arbitrary types of assets.

The naive approach

The most straightforward approach is to use AssetManager to load all your assets upfront and make the manager globally available. That way you’d be able to easily retrieve resources.

sharememe1348806647

public class MyGame extends Game {

	private AssetManager manager;

	@Override
	public void create() {
		super.create()

		manager = new AssetManager();
		loadAssets();
	}

	@Override
	public void dispose() {
		manager.dispose();
	}

	public AssetManager getAssetManager() {
		return manager;
	}

	private void loadAssets() {
		manager.load("data/player.png", Texture.class);
		manager.load("data/enemy.png", Texture.class);
		manager.load("data/bullet.png", Texture.class);
		manager.load("data/ui/uiskin.json", Skin.class);

		// ...

		manager.finishLoading();
	}
}

While this is a very comfortable thing to do and renders itself valid in most cases, we’re still loading assets that won’t be needed right away. Actually this snippet is taken almost as it was in my Ludum Dare #26 entry, and it was perfectly fine. It’s highly likely for you to get away with this. However, if you want to go the extra mile to achieve maximum performance, you know exactly what can be improved.

Slightly more advanced approaches

We only want to load the resources that will be used at a given time while avoiding being interned in a mental institution. Micromanaging everything is hard work and definitely not worth the headache in almost every situation. One question now arises.

What’s the ideal asset management granularity?

Obviously, the soft spot will be closely tied to the scope and genre in play but let’s list a few possibilities.

  • Everything at a time – Not an option
  • Menus, levels
  • Individual menu screen, individual level, all characters
  • Individual menu screen, individual level, needed characters
  • Individual menu screen, streaming level chunks, needed characters
Max Payne 3 (streaming) - COD (load screen)

Max Payne 3 (streaming) – COD (load screen)

Different games take different paths. On one hand you have Max Payne 3 which loads common assets upfront and then it seamlessly streams the rest as needed. On the other hand we find the Call of Duty example, where there’s a load screen masked behind a briefing before each level.

I’m a lazy bastard and that sounds like a lot of work

As you can see, it can get mental institution level of crazy in terms of deciding who loads what and when. Retrieving an asset you erroneously thought to be in memory could become an everyday issue. You want to simplify it.

The most annoying thing is to manually load each asset in the appropriate place and to handle the asynchronous process. Then why not gather resources and group them? Loading or freeing up an asset group by a single name would be much easier. I’ll leave the grouping policy entirely up to you to decide as it depends on your needs. However, it seems like the simple way to go is that each game Screen is responsible for loading and unloading its own resource group. If you think it’s going to take some time, then it might be appropriate to render a loading screen meanwhile. Moreover, you could have a base group of common assets that will always remain available.

Load sequence diagram

Load sequence diagram

Taking a data driven approach to group assets is like the cherry on top as it enables you to make adjustments to balance out the workload as you iterate on the process. I started using XML as file format but JSON or similar would also be fit. Here’s an example.

<assets >
	<group name="base">
		<asset type="com.siondream.core.animation.AnimationData" path="data/caveman.xml" />
		<asset type="com.siondream.core.physics.PhysicsData" path="data/caveman_physics.xml" />
		<asset type="com.badlogic.gdx.scenes.scene2d.ui.Skin" path="data/ui/uiskin.json" />
	</group>
</assets>

Qualified types and reflection are used to make the AssetManager#load calls.

Grouped Asset Manager code

Here’s the full implementation for my Asset class. It’s kind of a small layer on top of the libgdx AssetManager. Feel free to use it, bin it or change it. However, be advised that I don’t plan to offer further continued for it, this is simply to illustrate my approach.

public class Assets implements Disposable, AssetErrorListener {

	private static final String TAG = "Assets";
	private Logger logger;
	private AssetManager manager;
	private ObjectMap<String, Array<Asset>> groups;
	
	public Assets(String assetFile) {
		logger = new Logger(TAG, Logger.INFO);
		
		manager = new AssetManager();
		manager.setErrorListener(this);
		manager.setLoader(PhysicsData.class, new PhysicsLoader(new InternalFileHandleResolver()));
		manager.setLoader(AnimationData.class, new AnimationLoader(new InternalFileHandleResolver()));
		
		loadGroups(assetFile);
	}
		
	public void loadGroup(String groupName) {
		logger.info("loading group " + groupName);
		
		Array<Asset> assets = groups.get(groupName, null);
		
		if (assets != null) {
			for (Asset asset : assets) {
				manager.load(asset.path, asset.type);
			}
		}
		else {
			logger.error("error loading group " + groupName + ", not found");
		}
	}
	
	public void unloadGroup(String groupName) {
		logger.info("unloading group " + groupName);
		
		Array<Asset> assets = groups.get(groupName, null);
		
		if (assets != null) {
			for (Asset asset : assets) {
				if (manager.isLoaded(asset.path, asset.type)) {
					manager.unload(asset.path);
				}
			}
		}
		else {
			logger.error("error unloading group " + groupName + ", not found");
		}
	}
	
	public synchronized <T> T get(String fileName) {
		return manager.get(fileName);
	}
	
	public synchronized <T> T get(String fileName, Class<T> type) {
		return manager.get(fileName, type);
	}
	
	public boolean update() {
		return manager.update();
	}
	
	public void finishLoading() {
		manager.finishLoading();
	}
	
	public float getProgress() {
		return manager.getProgress();
	}

	@Override
	public void dispose() {
		logger.info("shutting down");
		manager.dispose();
	}
	
	@Override
	public void error(String fileName, Class type, Throwable throwable) {
		logger.error("error loading " + fileName);
	}
	
	private void loadGroups(String assetFile) {
		groups = new ObjectMap<String, Array<Asset>>();
		
		logger.info("loading file " + assetFile);
		
		try {
			XmlReader reader = new XmlReader();
			Element root = reader.parse(Gdx.files.internal(assetFile));

			for (Element groupElement : root.getChildrenByName("group")) {
				String groupName = groupElement.getAttribute("name", "base");
				
				if (groups.containsKey(groupName)) {
					logger.error("group " + groupName + " already exists, skipping");
					continue;
				}
				
				logger.info("registering group " + groupName);
				
				Array<Asset> assets = new Array<Asset>();
				
				for (Element assetElement : groupElement.getChildrenByName("asset")) {
					assets.add(new Asset(assetElement.getAttribute("type", ""),
										 assetElement.getAttribute("path", "")));
				}
				
				groups.put(groupName, assets);
			}
		}
		catch (Exception e) {
			logger.error("error loading file " + assetFile + " " + e.getMessage());
		}
	}
	
	private class Asset {
		public Class<?> type;
		public String path;
		
		public Asset(String type, String path) {
			try {
				this.type = Class.forName(type);
				this.path = path;
			} catch (ClassNotFoundException e) {
				logger.error("asset type " + type + " not found");
			}
		}
	}
}

Conclusions and further improvements

This small grouped asset manager will do the trick for me. Nevertheless I believe it could be improved in a few ways.

  • JSON format for faster parsing.
  • Automatically scan asset folders.
  • Associate file extensions to asset types.

Anyhow, I hope I made my point of showing some different approaches to asset management along their pros and cons. I’d love to hear your thoughts on the topic, and were you to find yourself disagreeing with me, by all means, share your point of view.

LD #26 – Lighbyrinth Postmortem

6

lightbyrinth

Last weekend was as sleep depriving as rewarding. While I could have easily been surrounded by the joys of procrastination and beer, I went for Ludum Dare #26, as announced in my previous post. Lightbyrinth is the result of that and, despite its flaws, I’m fairly happy with the outcome. Please don’t embarrass me by looking at the source, I was in a 48 hour rush.

Lightbyrinth is a light based maze which tries to stick to the “Minismalism” democratically voted theme. You control a small blue sphere that has to find the exit of the labyrinth it’s in. An infinite swarm of red spheres will try to stop you from doing so, reason why you should move fast. Beware though, as it’s quite dark and you will only see what’s in front of you.

HTML5 Powered Play Lightbyrinth

The game targets both desktops and WebGL friendly browsers, it’s written in Java using the mighty libgdx and the Box2DLights extension.

Here’s my humble postmortem of the experience.

Dynamic main menu!

Dynamic main menu!

Timeline

I may have a terribly bad memory but thanks to my notes, here’s an overview of my executed plan of action for those who might be interested.

Lightbyrinth's early design sketch

Lightbyrinth’s early design sketch

Saturday April 27th

  • [6.00am] Woke up, read the theme and introduced some caffeine in my body.
  • [6.30am - 7.00am] Game design on paper.
  • [7.00am - 10.00am] Basic project setup, level loading, character control, camera movement and maze exit.
  • [10.00am - 11.00am] Loading physics data, collisions callbacks setup.
  • [11.00am - 12.00pm] Box2DLights integration and GWT compatibility.
  • [12.00pm - 1.30pm] Lunch break!
  • [1.30pm - 2.30pm] Box2DLights configuration and tweaking for gameplay purposes.
  • [2.30pm -3.00pm] Enemy spawn points.
  • [3.00pm - 5.00pm] Pathfinder.
  • [5.00pm - 7.00pm] Path follower and local steering for collision avoidance.
  • [7.00pm - 9.00pm] Dinner break, and some TV shows!
  • [9.00pm - 10.00pm] Basic enemy behavior setup and tweaking.
  • [10.00pm - 10.30pm] Death condition.
  • [10.30pm - 12.00am] Shooting mechanic.

Sunday April 28th

  • [12.00am - 2.00am] Game director, procedural enemy spawning system.
  • [7.00am - 8.00am] Level progression.
  • [8.00am - 8.30am] Temporal level brightening up the level when the player is hurt .
  • [8.30am - 10.00am] In game tip system.
  • [10.00am - 12.00pm] In game HUD.
  • [12.00am - 1.00pm] Lunch break.
  • [1.00pm - 3.30pm] Main menu.
  • [3.30pm - 5.00pm] Tileset.
  • [5.00pm - 6.00pm] Sound effects.
  • [6.00pm - 7.00pm] Background music.
  • [7.00pm - 9.00pm] Dinner break.
  • [9.00pm - 12.00am] Level design.
  • [12.00am - 1.30am] Polish.
  • [1.30am - 2.00am] Submission and collapse!
The Lightbyrinth is a pretty dark place

The Lightbyrinth is a pretty dark place

What went right

  • Technology: using libgdx has proven to be the right decision once again. I had the very core of the game up and running in the first few hours thanks to its maps API sweetness and awesome Box2D integration. Its rapid iteration approach is a big win as well, as I could test it on my desktop without having to deploy it on a server every time.
  • KISS: some people didn’t like “Minimalism” as a theme. However, I think it ended up being the perfect way to enforce the Keep It Simple Stupid philosophy. It helped me focus on gameplay and feel rather than presentation, which was a clear mistake in my previous Ludum Dare attempt. The game boils down to: spheres as characters, two types of tiles for the maps and the lights, which were part of both aesthetic and mechanics.
  • Help system: people like to play and rate games after the compo but they certainly dislike reading through walls of texts to learn all the gameplay mechanics. Therefore, adding an in game data driven tip system was a plus. Little pieces of text would momentarily show at the top as new mechanics were introduced. For variety sake’s, tips were mixed with atmosphere creation messages. I’m no writer and that clearly shows, but hey, it wasn’t a bad idea at all.
  • Desktop and WebGL: libgdx multiplatform capabilities were extremely helpful in this case. You get Windows, Mac and Linux out of the box but that’s not all. Even though I had to invest some time in order to get Box2DLights working on GWT, it paid off big time. Having a WebGL version of your compo game available makes it much easier for people to give it a try. I know not downloading other’s people games when they’re desktop exclusive, kind of makes me a bad person. Ain’t nobody got time for that!
Holy bananas, I'm not alone in the Lightbyrinth!

Holy bananas, I’m not alone in the Lightbyrinth!

What went wrong

  • Camera: all the negative feedback I got agrees on the camera. It doesn’t follow the player fast enough, which makes seeing where you’re headed a not very pleasant experience. Bad time management, clearly should have dropped something to work on this.
  • Design polish: let’s get this out of the way, I’m no designer. Nevertheless, this would be a lame excuse for level design, I’m trying to get better, promise. The intent was to progressively introduce mechanics using little tips, which I did and that’s great. However, I feel like the maze and enemy spawning components could have been used much more intelligently. Reminder for self: practice your level design skills.
  • Bugs: a couple of vicious bugs slipped from grasp but didn’t go unnoticed by some people. It seems that you can still control the character in between levels, while there’s no physics, this could make you end up inside a wall, which is no good. Again, I should have allocated more polish time.
Reaching the goal just in time!

Reaching the goal just in time!

Conclusions

I care not about the Ludum Dare hangover, it was an amazing experience once more. I keep learning so many things about games development and extreme time management. Not to mention the wonderful community behind all this. We all know the Internet is a hazardous place full of mean people who like to destroy other folks’ work. Ludum Dare is nothing like that, of course not all the feedback was positive, but the not so good comments were all constructive and will help me get better.

On a side note, Mario from libgdx, put together a simple tool to search Ludum Dare games as an experiment to see how many people used the library. It’s ridiculously popular, shocker!

That’s brilliant. Now onto some games rating.

This time I’m going to try vote as many games as possible (30 entries so far). Towards that end, I think this Ludum Dare based drinking game could help a lot!

Ludum Dare #26: I’m in!

1

Ludum-Dare-featured-image-thumb-478x222-3939

Ludum Dare #24 was my first game jam, an experience I found most enjoyable. Sadly, I couldn’t make it to its 25th edition. However, unless something major happens, I’m totally in for Ludum Dare #26 which will take place next weekend. Aiming at respecting tradition I shall post my arsenal of choice, unsurprisingly, they are as follow.

  • Environment: Java + Eclipse.
  • Libraries: libgdx & friends, maybe something from my codebase.
  • Graphics: Inkscape, TexturePacker and Flash if I require animations.
  • Audio: Audacity and the dodgy effects produced by sfxr.
  • Others: depending on game details I could use Tiled for level design, Photoshop.

The goal is to finish a game that sucks as less as possible while retaining my mental sanity and some physical health. That means going for a jog each day and keeping the caffeine intake under control.

Keeping things short, I’ll be posting progress updates on Twitter all weekend and a proper postmortem article will follow on this very blog.

Don’t be a dick with the community

1

Open source is sometimes much more than just coding and committing to the most trendy repository at that point in time. It’s also about the community. When a piece of software grows, stops being a pet project and makes a certain impact, it is to be expected to see people flocking around it. Who knows, even other developers could start contributing via pull requests, documentation or support in your forums.

Awesome!

Indeed, but…

Well, sometimes projects can grow too much, its main developer may be trapped under a massive pile of work or simply doesn’t feel like investing so much time in it anymore, which is fair enough. However, some authors adopt quite an aggressive approach when it comes to dealing with their community and that is not so okay.

Hostile communities

Earlier this week a fellow programmer and countryman sent a pull request to Tiny Tiny RSS. Interestingly enough, that’s the Google Reader alternative I talked about less than a month ago. Sadly, he was received with rude comments and a complete lack of appreciation for his efforts. Take a couple of minutes to read through the brief conversation and judge by yourself.

I don’t want to get into the details of the pull request itself, but lets be over generous and assume my friend didn’t approach the initial problem the best way possible. Regardless of the quality of the patch, there’s no reason to drop all common courtesy except for, perhaps, enhancing, an already large ego.

Typical environment in a hostile community

Typical environment in a hostile community

Making this all about this particular case would be a bit ugly and, since there is a much obvious and better example, let’s bring it up. Linus Torvalds and the Linux Kernel! There are countless mailing list entries with serious insults coming from the controversial programmer aimed at contributors, who, in most cases, are renowned professionals. Surely he is an incredibly busy man and his project is titanic. Nevertheless, excuse me for saying that not even such situation legitimises being a dick.

It’s really not that hard to be polite

It goes without saying that a much better response for these situations be something like what follows.

Thanks for the pull request! However, I believe X is not an optimal solution cause of Y. If you could rework it and update the commit later, that’d be ace. Otherwise, we’ll try to address the underlying issue as soon as possible.

Even this is fine.

Sorry it took so long to review this. I’m sorry, I can’t accept this patch because of Y. This topic has already been discussed in the forums, please query the search tool before tackling a problem in the future. I’m too busy to fix this myself, feel free to try again.

See? That wasn’t too hard, was it?

Obviously, this guy, or anyone for that matter, can do whatever he pleases. The matter of whether his community would prosper under this environment or not, is a whole different story. I would imagine, a minority of them just don’t give a monkey’s. Ultimately, I guess some people are simply difficult to deal with. However, if you want to work in a community, I think it’s good to be aware of good practises, generally common sense and politeness work.

Healthy communities

Fortunately, there are plenty of cheerful and healthy communities. Some more than others, but my point is that they exist. I can’t say my experience with crowd powered open source development is great but I’ve been involved in a couple projects. As some of you may know, one of them is libgdx.

Libgdx’s forums, IRC channel and Github repository are full of friendly folks. As opposed to the previous examples, I’ve never seen a poisonous comment nor an insult. Actually it’s quite the opposite, for instance, all pull requests are nicely handled, even if they’re incorrect. People are constantly posting extensions, showcasing their work and the acid humor that floods the IRC is just brilliant.

Typical atmosphere in a healthy community

Typical atmosphere in a healthy community

That kind of atmosphere is truly splendid, as it encourages people to be more active. Ironically, the hazardous environment of hostile communities scare newcomers off because they think they’d be attacked as soon as they participate. Sadly, their negativity gives the open source world its current reputation of being swarmed by nerds with gigantic egos.

So what’s the moral of this story?

Don’t be a grumpy person, don’t be a dick, be nice to people. Also, on a side note, don’t let an unhealthy community knock down your morale.

Libraries, libgdx and GWT survival guide

6

As of today, I truly believe libgdx to be the best open source cross platform games framework when it comes to 2D. As if desktop, Android and iOS were not enough, it also comes with a WebGL backend. Luckily enough, this does not force you to maintain a Javascript version of your game code. Instead, it relies on Google Web Toolkit (GWT), which translates your Java project into optimized, obfuscated Javascript code.

OMG. How does this work?

Black, arcane magic. I would presume it sacrifices a dozen virgins during the third full moon of the year to make it happen. Just don’t ask.

Considerable win? Yes. Silver bullet? No.

This is utterly awesome for obvious reasons. Only, an insignificant amount of extra code is needed for your games to be fully playable from any WebGL browser without requiring any extra plugins. Take that Flash, Unity and the like!

However, as they say, not everything that glitters is gold. GWT doesn’t support every Java feature and this could potentially be a deal breaker depending on your existing codebase. Moreover, you could get unlucky and find out that the library that was about to make your life so much easier is not GWT friendly. Damn!

Last weekend, I was doing some work with libgdx that absolutely required browser compatibility. I happened to need a way of automatically placing tree nodes on a 2D surface. A smile was drawn on my face when I found the sexy treelayout library. Of course, it couldn’t be that easy, there’s no joy in victory when it hasn’t been preceded by some pain. I had to adapt it to the GWT build process and fix a few compatibility issues.

If anything, this made me realise that including new Java libraries in your WebGL libgdx game is not so straightforward. Hence, this small survival guide.

If you’re lucky, Setup UI

Chances are, you’ve setted up your project using the magnificent Setup UI tool. Aurelien Ribon’s free tool automatically creates cross platform libgdx Eclipse projects, including the libraries you want. Even though it has a system to add optional third party libraries, only a couple of them are available at the minute.

The super handy Setup UI

The super handy Setup UI

Long story short. As long as that external library you want is either Universal Tween Engine or Physics Body Editor loader, you’re good. Otherwise, read on, as you might have some work down the road. Hopefully, this situation will change in the future.

GWT build process 101

Bare in mind this is a guide so you can fix your library problem and move on, those who seek a proper explanation, I’d recommend the official documentation.

Let’s get going! You will find a very special file named GwtDefinition.gwt.xml under the main package folder in the HTML5 project. It’ll look something like this.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<module>
    <entry-point class="com.siondream.mygame.client.GwtLauncher"/>
    <set-configuration-property name="gdx.assetpath" value="../tranches-android/assets"/>
    <inherits name="MyGame"/>
    <inherits name="aurelienribon.tweenengine"/>
    <inherits name="com.badlogic.gdx.backends.gdx_backends_gwt"/>
</module>

Its main purpose is to tell the GTW compiler what modules the application depends on, and therefore, inherits from. As you can see, it’s also used to specify the entry point class and add configuration params such as the assets folder location. I’ve obviously made MyGame module up, this would correspond to your core project. Look under the src folder of your core project and you should find a MyGame.gwt.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<module>
	<source path="com/siondream/mygame" />
</module>

As you can see, the MyGame module contains a single source path which is located under the com.siondream.mygame package. If you wanted, you could add more source path entries in this file as long as they point to packages under the same project.

Now you’re a GWT expert, right?

Integrating a third party library

It’s not hard to guess what you need to add an external library to your GWT build process. You’ve guessed right, it’s necessary to add a new module inheritance entry in your GwtDefinitions.xml file that points to a module file, which in turn, points to where the source code is.

Does that mean that I can’t get away with just adding a third party jar to the build path?

Exactly. This is a bit of a downside because it requires you to import the whole library source into your workspace, and that means you need to be able to access the source in the first place.

Crap!

Well, it’s actually not too bad. In my particular case, I went to the treelayout Google Code repository, checked out the source using Tortoise SVN and imported the project, easy peasy. The next step would be to add a module definition file under the src folder in the treelayout project, let’s name it TreeLayout.gwt.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<module>
	<source path="org/abego/treelayout" />
</module>

Hang on, we’re almost there! Finally, add the inheritance entry to the GwtDefinitions.xml file.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<module>
    <entry-point class="com.siondream.tranches.client.GwtLauncher"/>
    <set-configuration-property name="gdx.assetpath" value="../tranches-android/assets"/>
    <inherits name="MyGame"/>
    <inherits name="TreeLayout"/>
    <inherits name="aurelienribon.tweenengine"/>
    <inherits name="com.badlogic.gdx.backends.gdx_backends_gwt"/>
</module>

Done! If you take 10 minutes to offer some prayers to $deity and press GWT everything should just work.

Note: after someone saying, “it just should just work”, I’ve come to realise that such situation is hardly ever the case.

Errors, expect them

Before carrying on with this section, I would always recommend to add these options to the GWT compiler.

-optimize 9 -strict

Optimize speaks for itself but strict means that the build process will bail out as soon as it encounters an error, rather than trying to keep going.

As long as you’ve followed the process, your library should be detected by the GWT compiler. However, like I’ve said before, GWT doesn’t support every Java feature. Every time you use an unsupported class or method you’ll get an error that looks like the following one.

[ERROR] Line 84: No source code is available for type java.util.Formatter; did you forget to inherit a required module?

This is going to hurt, isn’t it?

Again, GWT is not a silver bullet but it generally does a pretty good job. To tackle these issues you could try replacing every problematic class usage or method call by an equivalent, or an alternative of your own. Fear not, most Java stuff is there, the common troublesome areas I’ve come across are reflection, AWT and String.format().

Off you go

These steps helped me with treelayout integration and I hope they can aid you too. In my case I had to go through them all as the library used unsupported features. We’re done here, feel free to comment with your own experiences. Also, if you find any errors in the text, please point it out so it can be promptly fixed.

Go to Top