CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-badlogicgames-gdx--gdx-backend-gwt

GWT backend for libGDX enabling Java game development for web browsers through JavaScript compilation

Pending
Overview
Eval results
Files

preloader.mddocs/

Asset Preloading

The GWT backend includes a comprehensive asset preloading system designed specifically for web deployment. The preloader downloads and caches all game assets before the application starts, ensuring smooth gameplay without loading delays.

Core Preloader Classes

Preloader { .api }

public class Preloader {
    // Preloader state information
    public static class PreloaderState {
        public long loaded;    // Bytes loaded
        public long total;     // Total bytes to load
        public boolean hasEnded; // Loading complete
        
        public float getProgress();
        public boolean isComplete();
    }
    
    // Preloader callback interface
    public interface PreloaderCallback {
        void update(PreloaderState state);
        void error(String file);
    }
    
    // Constructor
    public Preloader(String assetsFileLocation);
    public Preloader();
    
    // Preloading control
    public void preload(String assetFileUrl, PreloaderCallback callback);
    public PreloaderState update();
    
    // Asset access after preloading
    public boolean contains(String path);
    public int length(String path);
    public boolean isText(String path);
    public boolean isAudio(String path);
    public boolean isImage(String path);
    public boolean isBinary(String path);
    
    // Asset reading
    public String getText(String path);
    public byte[] getBinary(String path);
    public ImageElement getImage(String path);
    
    // Asset management
    public void loadText(String url, AssetDownloader.AssetLoaderListener<String> assetLoaderListener);
    public void loadBinary(String url, AssetDownloader.AssetLoaderListener<Blob> assetLoaderListener);
    public void loadImage(String url, AssetDownloader.AssetLoaderListener<ImageElement> assetLoaderListener);
    public void loadAudio(String url, AssetDownloader.AssetLoaderListener<Void> assetLoaderListener);
}

AssetDownloader { .api }

public class AssetDownloader {
    // Asset loading interface
    public interface AssetLoaderListener<T> {
        void onProgress(double amount);
        void onFailure();
        void onSuccess(T result);
    }
    
    // Asset loading methods
    public void load(Array<Asset> assets, LoaderCallback<AssetDownloader> callback);
    public void loadText(String url, AssetLoaderListener<String> listener);
    public void loadBinary(String url, AssetLoaderListener<Blob> listener);
    public void loadImage(String url, AssetLoaderListener<ImageElement> listener);
    public void loadAudio(String url, AssetLoaderListener<Void> listener);
    
    // Asset information
    public boolean contains(String path);
    public boolean isText(String path);
    public boolean isBinary(String path);
    public boolean isAudio(String path);
    public boolean isImage(String path);
    
    // Asset access
    public String getText(String path);
    public ImageElement getImage(String path);
    public Blob getBinary(String path);
}

AssetFilter { .api }

public interface AssetFilter {
    boolean accept(String file, boolean isDirectory);
    
    // Pre-defined filter types
    AssetFilter IMAGE_FILTER = new AssetFilter() {
        public boolean accept(String file, boolean isDirectory) {
            return isDirectory || file.matches(".*\\.(png|jpg|jpeg|gif|bmp)$");
        }
    };
    
    AssetFilter AUDIO_FILTER = new AssetFilter() {
        public boolean accept(String file, boolean isDirectory) {
            return isDirectory || file.matches(".*\\.(ogg|mp3|wav|m4a)$");
        }
    };
    
    AssetFilter TEXT_FILTER = new AssetFilter() {
        public boolean accept(String file, boolean isDirectory) {
            return isDirectory || file.matches(".*\\.(txt|json|xml|csv)$");
        }
    };
}

DefaultAssetFilter { .api }

public class DefaultAssetFilter implements AssetFilter {
    public boolean accept(String file, boolean isDirectory) {
        if (isDirectory) return true;
        
        // Accept common game asset formats
        return file.matches(".*\\.(png|jpg|jpeg|gif|bmp|ogg|mp3|wav|m4a|txt|json|xml|fnt|pack|atlas)$");
    }
}

FileWrapper { .api }

public class FileWrapper {
    // File information
    public String path;
    public FileType type;
    public long length;
    public boolean isDirectory;
    
    // File content access
    public String read();
    public String readString();
    public byte[] readBytes();
    public long length();
    
    // Constructors
    public FileWrapper(String path, FileType type);
    public FileWrapper(String path, FileType type, long length, byte[] data);
}

Preloader Integration

Basic Preloader Setup

public class MyGameGwt extends GwtApplication {
    @Override
    public GwtApplicationConfiguration getConfig() {
        return new GwtApplicationConfiguration(800, 600);
    }

    @Override
    public ApplicationListener createApplicationListener() {
        return new MyGame();
    }
    
    @Override
    public Preloader createPreloader() {
        // Create preloader with assets list file
        return new Preloader("assets.txt");
    }
    
    @Override
    public PreloaderCallback getPreloaderCallback() {
        return new PreloaderCallback() {
            @Override
            public void update(PreloaderState state) {
                // Update loading progress display
                float progress = state.getProgress();
                updateLoadingScreen(progress);
                
                System.out.println("Loading: " + (int)(progress * 100) + "%");
            }
            
            @Override
            public void error(String file) {
                System.err.println("Failed to load asset: " + file);
            }
        };
    }
    
    private void updateLoadingScreen(float progress) {
        // Update your custom loading screen UI
        // This runs before your ApplicationListener.create() is called
    }
}

Assets File Format

The assets.txt file lists all assets to be preloaded:

# Asset list for preloader
# Format: path:type:size (size is optional)

# Images
images/player.png:i
images/enemy.png:i  
images/background.jpg:i
images/ui/button.png:i

# Audio files
audio/background.ogg:a
audio/jump.wav:a
audio/explosion.ogg:a

# Text/data files
data/levels.json:t
data/config.txt:t
fonts/arial.fnt:t

# Binary files
data/terrain.bin:b

# Type codes:
# i = image
# a = audio  
# t = text
# b = binary

Advanced Preloader Usage

Custom Asset Filtering

public class GameAssetGwt extends GwtApplication {
    @Override
    public Preloader createPreloader() {
        return new Preloader() {
            @Override
            protected AssetFilter getAssetFilter() {
                return new AssetFilter() {
                    @Override
                    public boolean accept(String file, boolean isDirectory) {
                        if (isDirectory) return true;
                        
                        // Only preload essential assets
                        if (file.startsWith("essential/")) return true;
                        if (file.endsWith(".png") || file.endsWith(".jpg")) return true;
                        if (file.endsWith(".ogg") || file.endsWith(".wav")) return true;
                        if (file.endsWith(".json") || file.endsWith(".fnt")) return true;
                        
                        return false;
                    }
                };
            }
        };
    }
}

Progress Tracking with Custom UI

public class LoadingScreenGwt extends GwtApplication {
    private ProgressBar progressBar;
    private Label statusLabel;
    
    @Override
    public PreloaderCallback getPreloaderCallback() {
        return new PreloaderCallback() {
            @Override
            public void update(PreloaderState state) {
                float progress = state.getProgress();
                long loaded = state.loaded;
                long total = state.total;
                
                // Update progress bar
                if (progressBar != null) {
                    progressBar.setValue(progress);
                }
                
                // Update status text
                if (statusLabel != null) {
                    String status = String.format("Loading... %d/%d KB (%.1f%%)", 
                                                 loaded / 1024, total / 1024, progress * 100);
                    statusLabel.setText(status);
                }
                
                // Custom loading stages
                if (progress < 0.3f) {
                    setLoadingStatus("Loading textures...");
                } else if (progress < 0.6f) {
                    setLoadingStatus("Loading audio...");
                } else if (progress < 0.9f) {
                    setLoadingStatus("Loading data...");
                } else {
                    setLoadingStatus("Initializing game...");
                }
            }
            
            @Override
            public void error(String file) {
                System.err.println("Loading failed for: " + file);
                setLoadingStatus("Error loading " + file);
            }
        };
    }
    
    private void setLoadingStatus(String status) {
        System.out.println(status);
        // Update your loading screen UI
    }
}

Asset Validation and Error Handling

public class RobustPreloaderGwt extends GwtApplication {
    private Set<String> failedAssets = new HashSet<>();
    private int retryCount = 0;
    private static final int MAX_RETRIES = 3;
    
    @Override
    public PreloaderCallback getPreloaderCallback() {
        return new PreloaderCallback() {
            @Override
            public void update(PreloaderState state) {
                if (state.hasEnded) {
                    if (!failedAssets.isEmpty() && retryCount < MAX_RETRIES) {
                        // Retry failed assets
                        retryCount++;
                        System.out.println("Retrying failed assets (attempt " + retryCount + ")");
                        retryFailedAssets();
                    } else {
                        // Proceed with missing assets or after max retries
                        proceedWithAvailableAssets();
                    }
                }
            }
            
            @Override
            public void error(String file) {
                failedAssets.add(file);
                System.err.println("Failed to load: " + file);
            }
        };
    }
    
    private void retryFailedAssets() {
        // Create new preloader for failed assets only
        Preloader retryPreloader = new Preloader();
        for (String failedAsset : failedAssets) {
            // Add failed assets to retry queue
            // Implementation depends on your specific needs
        }
    }
    
    private void proceedWithAvailableAssets() {
        if (failedAssets.isEmpty()) {
            System.out.println("All assets loaded successfully");
        } else {
            System.out.println("Proceeding with " + failedAssets.size() + " missing assets");
            
            // Log missing assets for debugging
            for (String missing : failedAssets) {
                System.out.println("Missing: " + missing);
            }
        }
        
        // Continue to game initialization
    }
}

Asset Management Best Practices

Optimal Asset Organization

// Organize assets for efficient preloading
public class AssetOrganizer {
    
    // Group assets by loading priority
    private static final String[] CRITICAL_ASSETS = {
        "images/loading_bg.png",      // Loading screen background
        "images/progress_bar.png",    // Progress bar graphics
        "fonts/ui_font.fnt"          // UI font
    };
    
    private static final String[] ESSENTIAL_ASSETS = {
        "images/player.png",
        "images/ui/buttons.pack",
        "audio/ui_sounds.ogg"
    };
    
    private static final String[] GAMEPLAY_ASSETS = {
        "images/enemies.pack",
        "images/levels.pack", 
        "audio/music.ogg",
        "data/levels.json"
    };
    
    public static void validateAssetStructure() {
        // Verify critical assets are available
        for (String asset : CRITICAL_ASSETS) {
            if (!Gdx.files.internal(asset).exists()) {
                throw new RuntimeException("Critical asset missing: " + asset);
            }
        }
        
        // Check essential assets
        int missingEssential = 0;
        for (String asset : ESSENTIAL_ASSETS) {
            if (!Gdx.files.internal(asset).exists()) {
                System.err.println("Essential asset missing: " + asset);
                missingEssential++;
            }
        }
        
        if (missingEssential > 0) {
            System.out.println("Warning: " + missingEssential + " essential assets missing");
        }
    }
}

Asset Size Optimization

public class AssetOptimizer {
    private static final long MAX_TOTAL_SIZE = 50 * 1024 * 1024; // 50MB limit
    private static final long MAX_SINGLE_FILE = 5 * 1024 * 1024;  // 5MB per file
    
    public static void analyzeAssetSizes() {
        long totalSize = 0;
        int oversizedFiles = 0;
        
        String[] assetDirs = {"images/", "audio/", "data/"};
        
        for (String dir : assetDirs) {
            FileHandle dirHandle = Gdx.files.internal(dir);
            if (dirHandle.exists() && dirHandle.isDirectory()) {
                for (FileHandle file : dirHandle.list()) {
                    long fileSize = file.length();
                    totalSize += fileSize;
                    
                    if (fileSize > MAX_SINGLE_FILE) {
                        System.out.println("Oversized file: " + file.path() + 
                                         " (" + (fileSize / 1024) + " KB)");
                        oversizedFiles++;
                    }
                }
            }
        }
        
        System.out.println("Total asset size: " + (totalSize / 1024 / 1024) + " MB");
        System.out.println("Oversized files: " + oversizedFiles);
        
        if (totalSize > MAX_TOTAL_SIZE) {
            System.out.println("WARNING: Total assets exceed recommended size limit");
        }
    }
    
    public static void generateOptimizedAssetsList() {
        // Create optimized assets.txt based on file sizes and importance
        StringBuilder assetsList = new StringBuilder();
        assetsList.append("# Optimized assets list\n");
        assetsList.append("# Generated automatically\n\n");
        
        // Add critical assets first
        assetsList.append("# Critical assets (loaded first)\n");
        addAssetsToList(assetsList, "critical/", "i");
        
        // Add essential assets
        assetsList.append("\n# Essential gameplay assets\n");
        addAssetsToList(assetsList, "essential/", "i");
        
        // Add optional assets
        assetsList.append("\n# Optional enhancement assets\n");
        addAssetsToList(assetsList, "optional/", "i");
        
        // Write to file
        Gdx.files.local("generated_assets.txt").writeString(assetsList.toString(), false);
    }
    
    private static void addAssetsToList(StringBuilder list, String directory, String type) {
        FileHandle dir = Gdx.files.internal(directory);
        if (dir.exists() && dir.isDirectory()) {
            for (FileHandle file : dir.list()) {
                if (!file.isDirectory()) {
                    list.append(file.path()).append(":").append(type).append("\n");
                }
            }
        }
    }
}

Lazy Loading Strategy

// For very large games, implement lazy loading after initial preload
public class LazyAssetLoader {
    private Map<String, Boolean> loadedChunks = new HashMap<>();
    private AssetDownloader downloader = new AssetDownloader();
    
    public void loadChunk(String chunkName, Runnable onComplete) {
        if (loadedChunks.getOrDefault(chunkName, false)) {
            // Chunk already loaded
            if (onComplete != null) onComplete.run();
            return;
        }
        
        String[] chunkAssets = getChunkAssets(chunkName);
        loadAssetsBatch(chunkAssets, () -> {
            loadedChunks.put(chunkName, true);
            System.out.println("Chunk loaded: " + chunkName);
            if (onComplete != null) onComplete.run();
        });
    }
    
    private String[] getChunkAssets(String chunkName) {
        switch (chunkName) {
            case "level1":
                return new String[]{"images/level1_bg.png", "audio/level1_music.ogg"};
            case "level2": 
                return new String[]{"images/level2_bg.png", "audio/level2_music.ogg"};
            case "powerups":
                return new String[]{"images/powerups.pack", "audio/powerup_sfx.ogg"};
            default:
                return new String[0];
        }
    }
    
    private void loadAssetsBatch(String[] assets, Runnable onComplete) {
        // Implementation for batch loading assets after initial preload
        // This would use HTTP requests to download additional assets
        System.out.println("Loading " + assets.length + " additional assets...");
        
        // Simulate async loading
        Timer.schedule(new Timer.Task() {
            @Override
            public void run() {
                if (onComplete != null) onComplete.run();
            }
        }, 1.0f); // 1 second delay simulation
    }
}

Web-Specific Preloader Considerations

Browser Caching Integration

// The preloader works with browser caching for optimal performance
public class CacheAwarePreloader extends Preloader {
    public CacheAwarePreloader(String assetsFile) {
        super(assetsFile);
        
        // Assets are automatically cached by browser
        // Subsequent loads will be much faster
        // Use cache-busting for asset updates
    }
    
    // Generate cache-busting URLs for asset updates
    private String addCacheBuster(String assetUrl, String version) {
        return assetUrl + "?v=" + version;
    }
}

Memory Management

// Preloader manages memory efficiently for web deployment
public class MemoryEfficientPreloader {
    
    public static void optimizeForMemory() {
        // The GWT preloader automatically:
        // 1. Streams large files instead of loading entirely into memory
        // 2. Compresses text assets
        // 3. Uses efficient binary formats
        // 4. Releases intermediate loading buffers
        
        System.out.println("Preloader optimized for web memory constraints");
    }
    
    public static void monitorMemoryUsage() {
        // Check available memory during preloading
        long heapSize = Runtime.getRuntime().totalMemory();
        long usedMemory = heapSize - Runtime.getRuntime().freeMemory();
        
        System.out.println("Memory usage: " + (usedMemory / 1024 / 1024) + " MB");
        
        if (usedMemory > heapSize * 0.8) {
            System.out.println("WARNING: High memory usage during asset loading");
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-badlogicgames-gdx--gdx-backend-gwt

docs

application.md

audio.md

files.md

graphics.md

index.md

input.md

networking.md

preloader.md

webaudio.md

widgets.md

tile.json