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

networking.mddocs/

Networking

The GWT backend provides HTTP-based networking through browser APIs. Due to web security restrictions, only HTTP/HTTPS requests are supported, with no TCP/UDP socket capabilities.

Core Networking Classes

GwtNet { .api }

public class GwtNet implements Net {
    // HTTP requests
    public void sendHttpRequest(HttpRequest httpRequest, HttpResponseListener httpResponseListener);
    public void cancelHttpRequest(HttpRequest httpRequest);
    
    // Browser integration
    public void openURI(String URI);
    
    // Unsupported operations (throw UnsupportedOperationException)
    public ServerSocket newServerSocket(Protocol protocol, String hostname, int port, ServerSocketHints hints);
    public ServerSocket newServerSocket(Protocol protocol, int port, ServerSocketHints hints);
    public Socket newClientSocket(Protocol protocol, String host, int port, SocketHints hints);
}

HTTP Request Support

Basic HTTP Operations

// GET request example
HttpRequest httpGet = new HttpRequest(HttpMethods.GET);
httpGet.setUrl("https://api.example.com/data");
httpGet.setHeader("User-Agent", "MyGame/1.0");

Gdx.net.sendHttpRequest(httpGet, new HttpResponseListener() {
    @Override
    public void handleHttpResponse(HttpResponse httpResponse) {
        int statusCode = httpResponse.getStatus().getStatusCode();
        if (statusCode == 200) {
            String responseData = httpResponse.getResultAsString();
            // Handle successful response
            processApiData(responseData);
        } else {
            System.err.println("HTTP Error: " + statusCode);
        }
    }
    
    @Override
    public void failed(Throwable t) {
        System.err.println("Request failed: " + t.getMessage());
    }
    
    @Override
    public void cancelled() {
        System.out.println("Request was cancelled");
    }
});

// POST request example
HttpRequest httpPost = new HttpRequest(HttpMethods.POST);
httpPost.setUrl("https://api.example.com/submit");
httpPost.setHeader("Content-Type", "application/json");
httpPost.setContent("{\"score\": 1000, \"player\": \"Alice\"}");

Gdx.net.sendHttpRequest(httpPost, new HttpResponseListener() {
    @Override
    public void handleHttpResponse(HttpResponse httpResponse) {
        if (httpResponse.getStatus().getStatusCode() == 201) {
            System.out.println("Score submitted successfully");
        }
    }
    
    @Override
    public void failed(Throwable t) {
        System.err.println("Score submission failed: " + t.getMessage());
    }
    
    @Override
    public void cancelled() {
        System.out.println("Score submission cancelled");
    }
});

Usage Examples

Game API Integration

public class GameAPI {
    private static final String BASE_URL = "https://api.mygame.com";
    private static final String API_KEY = "your-api-key";
    
    public void submitHighScore(int score, String playerName, ApiCallback<Boolean> callback) {
        HttpRequest request = new HttpRequest(HttpMethods.POST);
        request.setUrl(BASE_URL + "/scores");
        request.setHeader("Authorization", "Bearer " + API_KEY);
        request.setHeader("Content-Type", "application/json");
        
        String jsonData = String.format(
            "{\"player\": \"%s\", \"score\": %d, \"timestamp\": %d}",
            playerName, score, System.currentTimeMillis()
        );
        request.setContent(jsonData);
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                boolean success = httpResponse.getStatus().getStatusCode() == 200;
                callback.onResult(success);
            }
            
            @Override
            public void failed(Throwable t) {
                callback.onError(t.getMessage());
            }
            
            @Override
            public void cancelled() {
                callback.onError("Request cancelled");
            }
        });
    }
    
    public void getLeaderboard(ApiCallback<LeaderboardData> callback) {
        HttpRequest request = new HttpRequest(HttpMethods.GET);
        request.setUrl(BASE_URL + "/leaderboard?limit=10");
        request.setHeader("Authorization", "Bearer " + API_KEY);
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                if (httpResponse.getStatus().getStatusCode() == 200) {
                    String jsonResponse = httpResponse.getResultAsString();
                    LeaderboardData data = parseLeaderboard(jsonResponse);
                    callback.onResult(data);
                } else {
                    callback.onError("HTTP " + httpResponse.getStatus().getStatusCode());
                }
            }
            
            @Override
            public void failed(Throwable t) {
                callback.onError(t.getMessage());
            }
            
            @Override
            public void cancelled() {
                callback.onError("Request cancelled");
            }
        });
    }
    
    private LeaderboardData parseLeaderboard(String json) {
        // Parse JSON response into LeaderboardData object
        // Can use libGDX Json class or manual parsing
        return new LeaderboardData();
    }
    
    public interface ApiCallback<T> {
        void onResult(T result);
        void onError(String error);
    }
    
    public static class LeaderboardData {
        public String[] playerNames;
        public int[] scores;
        public long[] timestamps;
    }
}

File Download System

public class AssetDownloader {
    private Map<String, HttpRequest> activeRequests = new HashMap<>();
    
    public void downloadAsset(String url, String localPath, DownloadCallback callback) {
        HttpRequest request = new HttpRequest(HttpMethods.GET);
        request.setUrl(url);
        
        // Store request for potential cancellation
        activeRequests.put(localPath, request);
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                activeRequests.remove(localPath);
                
                if (httpResponse.getStatus().getStatusCode() == 200) {
                    try {
                        // Save downloaded data to local storage
                        byte[] data = httpResponse.getResult();
                        FileHandle localFile = Gdx.files.local(localPath);
                        localFile.writeBytes(data, false);
                        
                        callback.onDownloadComplete(localPath);
                    } catch (Exception e) {
                        callback.onDownloadFailed(localPath, e.getMessage());
                    }
                } else {
                    callback.onDownloadFailed(localPath, "HTTP " + httpResponse.getStatus().getStatusCode());
                }
            }
            
            @Override
            public void failed(Throwable t) {
                activeRequests.remove(localPath);
                callback.onDownloadFailed(localPath, t.getMessage());
            }
            
            @Override
            public void cancelled() {
                activeRequests.remove(localPath);
                callback.onDownloadCancelled(localPath);
            }
        });
    }
    
    public void cancelDownload(String localPath) {
        HttpRequest request = activeRequests.get(localPath);
        if (request != null) {
            Gdx.net.cancelHttpRequest(request);
        }
    }
    
    public void cancelAllDownloads() {
        for (HttpRequest request : activeRequests.values()) {
            Gdx.net.cancelHttpRequest(request);
        }
        activeRequests.clear();
    }
    
    public interface DownloadCallback {
        void onDownloadComplete(String localPath);
        void onDownloadFailed(String localPath, String error);
        void onDownloadCancelled(String localPath);
    }
}

Configuration Server Integration

public class ConfigManager {
    private static final String CONFIG_URL = "https://config.mygame.com/settings.json";
    private JsonValue serverConfig;
    
    public void loadServerConfig(Runnable onComplete) {
        HttpRequest request = new HttpRequest(HttpMethods.GET);
        request.setUrl(CONFIG_URL);
        request.setHeader("Accept", "application/json");
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                if (httpResponse.getStatus().getStatusCode() == 200) {
                    try {
                        String jsonString = httpResponse.getResultAsSt​ring();
                        Json json = new Json();
                        serverConfig = json.fromJson(JsonValue.class, jsonString);
                        
                        System.out.println("Server config loaded successfully");
                        if (onComplete != null) onComplete.run();
                    } catch (Exception e) {
                        System.err.println("Failed to parse server config: " + e.getMessage());
                        useDefaultConfig();
                        if (onComplete != null) onComplete.run();
                    }
                } else {
                    System.err.println("Failed to load server config: HTTP " + httpResponse.getStatus().getStatusCode());
                    useDefaultConfig();
                    if (onComplete != null) onComplete.run();
                }
            }
            
            @Override
            public void failed(Throwable t) {
                System.err.println("Server config request failed: " + t.getMessage());
                useDefaultConfig();
                if (onComplete != null) onComplete.run();
            }
            
            @Override
            public void cancelled() {
                System.out.println("Server config request cancelled");
                useDefaultConfig();
                if (onComplete != null) onComplete.run();
            }
        });
    }
    
    public String getConfigString(String key, String defaultValue) {
        if (serverConfig != null && serverConfig.has(key)) {
            return serverConfig.getString(key, defaultValue);
        }
        return defaultValue;
    }
    
    public int getConfigInt(String key, int defaultValue) {
        if (serverConfig != null && serverConfig.has(key)) {
            return serverConfig.getInt(key, defaultValue);
        }
        return defaultValue;
    }
    
    public boolean getConfigBoolean(String key, boolean defaultValue) {
        if (serverConfig != null && serverConfig.has(key)) {
            return serverConfig.getBoolean(key, defaultValue);
        }
        return defaultValue;
    }
    
    private void useDefaultConfig() {
        // Create default configuration
        Json json = new Json();
        String defaultJson = "{\"maxPlayers\": 4, \"gameMode\": \"classic\", \"enableFeatureX\": true}";
        serverConfig = json.fromJson(JsonValue.class, defaultJson);
    }
}

Browser Integration

public class BrowserIntegration {
    // Open URLs in browser
    public static void openWebsite(String url) {
        try {
            Gdx.net.openURI(url);
            System.out.println("Opened URL: " + url);
        } catch (Exception e) {
            System.err.println("Failed to open URL: " + e.getMessage());
        }
    }
    
    // Open social media sharing
    public static void shareScore(int score) {
        String message = "I just scored " + score + " points in MyGame!";
        String twitterUrl = "https://twitter.com/intent/tweet?text=" + 
                           java.net.URLEncoder.encode(message, "UTF-8");
        openWebsite(twitterUrl);
    }
    
    // Open game store page
    public static void openStorePage() {
        openWebsite("https://store.example.com/mygame");
    }
    
    // Open support/feedback page
    public static void openSupport() {
        openWebsite("https://support.mygame.com");
    }
}

Analytics Integration

public class Analytics {
    private static final String ANALYTICS_URL = "https://analytics.mygame.com/events";
    private static final String SESSION_ID = generateSessionId();
    
    public static void trackEvent(String eventName, Map<String, Object> properties) {
        HttpRequest request = new HttpRequest(HttpMethods.POST);
        request.setUrl(ANALYTICS_URL);
        request.setHeader("Content-Type", "application/json");
        
        // Build event data
        Json json = new Json();
        Map<String, Object> eventData = new HashMap<>();
        eventData.put("event", eventName);
        eventData.put("sessionId", SESSION_ID);
        eventData.put("timestamp", System.currentTimeMillis());
        eventData.put("properties", properties);
        
        String jsonContent = json.toJson(eventData);
        request.setContent(jsonContent);
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                // Analytics requests typically don't need response handling
                if (httpResponse.getStatus().getStatusCode() != 200) {
                    System.err.println("Analytics event failed: " + httpResponse.getStatus().getStatusCode());
                }
            }
            
            @Override
            public void failed(Throwable t) {
                System.err.println("Analytics event failed: " + t.getMessage());
            }
            
            @Override
            public void cancelled() {
                // Ignore cancellations for analytics
            }
        });
    }
    
    public static void trackLevelStart(int level) {
        Map<String, Object> props = new HashMap<>();
        props.put("level", level);
        trackEvent("level_start", props);
    }
    
    public static void trackLevelComplete(int level, int score, float time) {
        Map<String, Object> props = new HashMap<>();
        props.put("level", level);
        props.put("score", score);
        props.put("completionTime", time);
        trackEvent("level_complete", props);
    }
    
    public static void trackPurchase(String itemId, float price) {
        Map<String, Object> props = new HashMap<>();
        props.put("itemId", itemId);
        props.put("price", price);
        trackEvent("purchase", props);
    }
    
    private static String generateSessionId() {
        return "session_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 10000);
    }
}

Web-Specific Networking Considerations

CORS (Cross-Origin Resource Sharing)

// Browser CORS policy affects HTTP requests
public class CORSHelper {
    // Only requests to same origin are always allowed
    // Cross-origin requests require server CORS headers
    
    public static void makeCORSRequest(String url) {
        // Server must include appropriate CORS headers:
        // Access-Control-Allow-Origin: *
        // Access-Control-Allow-Methods: GET, POST, PUT, DELETE
        // Access-Control-Allow-Headers: Content-Type, Authorization
        
        HttpRequest request = new HttpRequest(HttpMethods.GET);
        request.setUrl(url);
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                // Handle response
            }
            
            @Override
            public void failed(Throwable t) {
                // CORS failures often appear as network errors
                System.err.println("Request failed (possibly CORS): " + t.getMessage());
            }
            
            @Override
            public void cancelled() {
                // Handle cancellation
            }
        });
    }
}

Security Limitations

// Web security model limitations:

// ✓ Supported: HTTP/HTTPS requests
HttpRequest httpRequest = new HttpRequest(HttpMethods.GET);
httpRequest.setUrl("https://api.example.com/data");

// ✗ NOT supported: TCP/UDP sockets
try {
    Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, "example.com", 8080, null);
    // Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("TCP sockets not supported on web");
}

// ✗ NOT supported: Server sockets
try {
    ServerSocket serverSocket = Gdx.net.newServerSocket(Net.Protocol.TCP, 8080, null);
    // Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Server sockets not supported on web");
}

// ✓ Supported: Opening URLs in browser
Gdx.net.openURI("https://example.com");

Request Limitations

// Browser request limitations and best practices:

public class RequestManager {
    private static final int MAX_CONCURRENT_REQUESTS = 6; // Browser limit
    private int activeRequests = 0;
    private Queue<Runnable> requestQueue = new LinkedList<>();
    
    public void makeRequest(HttpRequest request, HttpResponseListener listener) {
        if (activeRequests < MAX_CONCURRENT_REQUESTS) {
            executeRequest(request, listener);
        } else {
            // Queue request if at browser limit
            requestQueue.offer(() -> executeRequest(request, listener));
        }
    }
    
    private void executeRequest(HttpRequest request, HttpResponseListener listener) {
        activeRequests++;
        
        Gdx.net.sendHttpRequest(request, new HttpResponseListener() {
            @Override
            public void handleHttpResponse(HttpResponse httpResponse) {
                activeRequests--;
                processQueue();
                listener.handleHttpResponse(httpResponse);
            }
            
            @Override
            public void failed(Throwable t) {
                activeRequests--;
                processQueue();
                listener.failed(t);
            }
            
            @Override
            public void cancelled() {
                activeRequests--;
                processQueue();
                listener.cancelled();
            }
        });
    }
    
    private void processQueue() {
        if (!requestQueue.isEmpty() && activeRequests < MAX_CONCURRENT_REQUESTS) {
            Runnable nextRequest = requestQueue.poll();
            nextRequest.run();
        }
    }
}

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