GWT backend for libGDX enabling Java game development for web browsers through JavaScript compilation
—
The GWT backend provides browser-compatible file system access with limitations imposed by web security. It supports internal assets, local storage files, and integrates with the asset preloading system for optimal web performance.
public class GwtFiles implements Files {
// File handle creation
public FileHandle internal(String path);
public FileHandle external(String path); // Not supported, throws exception
public FileHandle absolute(String path); // Not supported, throws exception
public FileHandle local(String path);
public FileHandle classpath(String path);
// Storage availability
public boolean isExternalStorageAvailable(); // Always returns false
public boolean isLocalStorageAvailable(); // Checks localStorage support
// Storage paths
public String getLocalStoragePath();
public String getExternalStoragePath(); // Not supported
}public class GwtFileHandle extends FileHandle {
// File operations
public boolean exists();
public boolean isDirectory();
public FileHandle child(String name);
public FileHandle parent();
public FileHandle sibling(String name);
// File information
public String name();
public String nameWithoutExtension();
public String extension();
public String path();
public String pathWithoutExtension();
// Type checking
public FileType type();
public boolean isInternal();
public boolean isExternal();
public boolean isAbsolute();
public boolean isLocal();
// Reading operations
public InputStream read();
public BufferedInputStream read(int bufferSize);
public Reader reader();
public Reader reader(String charset);
public String readString();
public String readString(String charset);
public byte[] readBytes();
// Length and modification
public long length();
public long lastModified();
// Writing operations (limited support)
public OutputStream write(boolean append);
public OutputStream write(boolean append, int bufferSize);
public Writer writer(boolean append);
public Writer writer(boolean append, String charset);
public void writeString(String string, boolean append);
public void writeString(String string, boolean append, String charset);
public void writeBytes(byte[] bytes, boolean append);
public void writeBytes(byte[] bytes, int offset, int length, boolean append);
// Directory operations (limited support)
public FileHandle[] list();
public FileHandle[] list(FilenameFilter filter);
public FileHandle[] list(String suffix);
public boolean deleteDirectory();
public void emptyDirectory();
public void emptyDirectory(boolean preserveTree);
// File operations
public void copyTo(FileHandle dest);
public void moveTo(FileHandle dest);
public boolean delete();
public boolean mkdirs();
// Temporary files (not supported)
public File file(); // Throws UnsupportedOperationException
}Internal files are read-only assets bundled with the application:
// Access bundled game assets
FileHandle textureFile = Gdx.files.internal("images/player.png");
FileHandle audioFile = Gdx.files.internal("audio/background.ogg");
FileHandle dataFile = Gdx.files.internal("data/levels.json");
// Read asset content
String levelData = Gdx.files.internal("data/level1.json").readString();
byte[] imageData = Gdx.files.internal("images/sprite.png").readBytes();
// Assets must be included in the preloader for web deployment
// See preloader.md for asset management detailsLocal files use browser localStorage for persistent data:
// Save game progress to localStorage
FileHandle saveFile = Gdx.files.local("save/progress.json");
saveFile.writeString("{\"level\": 5, \"score\": 1000}", false);
// Load saved data
if (saveFile.exists()) {
String saveData = saveFile.readString();
// Parse and use save data
}
// Local storage has size limitations (typically 5-10MB)
// Store only essential data like saves, preferences, etc.// Web security restrictions limit file system access:
// ✓ Supported: Read internal assets
FileHandle asset = Gdx.files.internal("data/config.txt");
String config = asset.readString();
// ✓ Supported: Read/write local storage
FileHandle local = Gdx.files.local("settings.json");
local.writeString("{\"volume\": 0.8}", false);
// ✗ NOT supported: External file access
try {
FileHandle external = Gdx.files.external("documents/file.txt");
// Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("External files not supported on web");
}
// ✗ NOT supported: Absolute file paths
try {
FileHandle absolute = Gdx.files.absolute("/home/user/file.txt");
// Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Absolute paths not supported on web");
}public class AssetManager {
private Texture playerTexture;
private Sound jumpSound;
private String levelData;
public void loadAssets() {
// Load textures (must be preloaded)
playerTexture = new Texture(Gdx.files.internal("sprites/player.png"));
// Load audio (must be preloaded)
jumpSound = Gdx.audio.newSound(Gdx.files.internal("audio/jump.wav"));
// Load text data
levelData = Gdx.files.internal("data/level1.json").readString();
}
public void disposeAssets() {
if (playerTexture != null) playerTexture.dispose();
if (jumpSound != null) jumpSound.dispose();
}
}public class SaveSystem {
private static final String SAVE_FILE = "game_save.json";
public static class SaveData {
public int level;
public int score;
public boolean[] unlockedLevels;
public float musicVolume;
public float sfxVolume;
}
public void saveProgress(SaveData data) {
try {
// Convert save data to JSON
String json = convertToJson(data);
// Write to local storage
FileHandle saveFile = Gdx.files.local(SAVE_FILE);
saveFile.writeString(json, false);
System.out.println("Game saved successfully");
} catch (Exception e) {
System.err.println("Failed to save game: " + e.getMessage());
}
}
public SaveData loadProgress() {
try {
FileHandle saveFile = Gdx.files.local(SAVE_FILE);
if (saveFile.exists()) {
String json = saveFile.readString();
return parseFromJson(json);
} else {
// Return default save data
return createDefaultSave();
}
} catch (Exception e) {
System.err.println("Failed to load game: " + e.getMessage());
return createDefaultSave();
}
}
public boolean hasSavedGame() {
return Gdx.files.local(SAVE_FILE).exists();
}
public void deleteSave() {
FileHandle saveFile = Gdx.files.local(SAVE_FILE);
if (saveFile.exists()) {
saveFile.delete();
}
}
private SaveData createDefaultSave() {
SaveData data = new SaveData();
data.level = 1;
data.score = 0;
data.unlockedLevels = new boolean[10];
data.unlockedLevels[0] = true; // First level unlocked
data.musicVolume = 0.8f;
data.sfxVolume = 1.0f;
return data;
}
private String convertToJson(SaveData data) {
// Implement JSON serialization
// Can use libGDX Json class or manual string building
return "{}"; // Placeholder
}
private SaveData parseFromJson(String json) {
// Implement JSON deserialization
// Can use libGDX Json class or manual parsing
return new SaveData(); // Placeholder
}
}public class ConfigManager {
private static final String CONFIG_FILE = "config.properties";
private Properties config;
public void loadConfig() {
config = new Properties();
try {
FileHandle configFile = Gdx.files.local(CONFIG_FILE);
if (configFile.exists()) {
// Load existing config
String configText = configFile.readString();
// Parse properties format
parseProperties(configText);
} else {
// Create default config
setDefaultConfig();
saveConfig();
}
} catch (Exception e) {
System.err.println("Failed to load config: " + e.getMessage());
setDefaultConfig();
}
}
public void saveConfig() {
try {
String configText = propertiesToString();
FileHandle configFile = Gdx.files.local(CONFIG_FILE);
configFile.writeString(configText, false);
} catch (Exception e) {
System.err.println("Failed to save config: " + e.getMessage());
}
}
public String getString(String key, String defaultValue) {
return config.getProperty(key, defaultValue);
}
public int getInt(String key, int defaultValue) {
try {
return Integer.parseInt(config.getProperty(key, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public float getFloat(String key, float defaultValue) {
try {
return Float.parseFloat(config.getProperty(key, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public boolean getBoolean(String key, boolean defaultValue) {
return Boolean.parseBoolean(config.getProperty(key, String.valueOf(defaultValue)));
}
public void setString(String key, String value) {
config.setProperty(key, value);
}
public void setInt(String key, int value) {
config.setProperty(key, String.valueOf(value));
}
public void setFloat(String key, float value) {
config.setProperty(key, String.valueOf(value));
}
public void setBoolean(String key, boolean value) {
config.setProperty(key, String.valueOf(value));
}
private void setDefaultConfig() {
config = new Properties();
config.setProperty("music_volume", "0.8");
config.setProperty("sfx_volume", "1.0");
config.setProperty("fullscreen", "false");
config.setProperty("difficulty", "normal");
}
private void parseProperties(String text) {
// Implement properties file parsing
String[] lines = text.split("\n");
for (String line : lines) {
line = line.trim();
if (!line.isEmpty() && !line.startsWith("#")) {
int equals = line.indexOf('=');
if (equals > 0) {
String key = line.substring(0, equals).trim();
String value = line.substring(equals + 1).trim();
config.setProperty(key, value);
}
}
}
}
private String propertiesToString() {
StringBuilder sb = new StringBuilder();
for (Object key : config.keySet()) {
sb.append(key).append("=").append(config.getProperty((String) key)).append("\n");
}
return sb.toString();
}
}public class AssetValidator {
public static boolean validateAssets() {
String[] requiredAssets = {
"images/player.png",
"images/enemy.png",
"audio/background.ogg",
"audio/jump.wav",
"data/levels.json"
};
boolean allAssetsExist = true;
for (String assetPath : requiredAssets) {
FileHandle asset = Gdx.files.internal(assetPath);
if (!asset.exists()) {
System.err.println("Missing required asset: " + assetPath);
allAssetsExist = false;
}
}
return allAssetsExist;
}
public static void listAvailableAssets(String directory) {
try {
FileHandle dir = Gdx.files.internal(directory);
if (dir.exists() && dir.isDirectory()) {
FileHandle[] files = dir.list();
System.out.println("Assets in " + directory + ":");
for (FileHandle file : files) {
System.out.println(" " + file.path());
}
}
} catch (Exception e) {
System.err.println("Cannot list directory: " + directory);
}
}
}// Browser localStorage typically has 5-10MB limit
public class StorageManager {
public static boolean checkStorageSpace() {
try {
// Test write to check available space
FileHandle testFile = Gdx.files.local("test_space.tmp");
testFile.writeString("test", false);
testFile.delete();
return true;
} catch (Exception e) {
System.err.println("Storage space check failed: " + e.getMessage());
return false;
}
}
public static long estimateStorageUsage() {
// Estimate current localStorage usage
// This is approximate as there's no direct API
try {
FileHandle[] localFiles = Gdx.files.local("").list();
long totalSize = 0;
for (FileHandle file : localFiles) {
totalSize += file.length();
}
return totalSize;
} catch (Exception e) {
return -1; // Unable to estimate
}
}
}// Assets must be preloaded for web deployment
// This is handled automatically by the preloader system
// See preloader.md for complete details
public void loadPreloadedAssets() {
// These assets must be listed in assets.txt for preloader
Texture texture = new Texture(Gdx.files.internal("images/sprite.png"));
Sound sound = Gdx.audio.newSound(Gdx.files.internal("audio/effect.wav"));
String data = Gdx.files.internal("data/config.json").readString();
// Assets not in preloader will fail to load or cause delays
}Install with Tessl CLI
npx tessl i tessl/maven-com-badlogicgames-gdx--gdx-backend-gwt