====== Comunicacions en libGDX ====== Aquest article ve de [[jocs libGDX]] on implementem jocs multiplataforma. libGDX es programa en Java, per tant podriem emprar les llibreries estàndard de comunicació HTTP o WebSockets. Però si utilitzem les pròpies de liBGDX ens facilitarà el codi, ja que ens estalviarem la gestió dels //threads// de comunicació necessaris a [[Android]] explicats a [[Android Threads]]. Llibreries de comunicacions: * [[https://libgdx.com/wiki/networking|libGDX Networking doc]] per a comunicacions HTTP. * [[https://github.com/MrStahlfelge/gdx-websockets|WebSockets plugin per a libGDX]] que ens permetran accions en temps real 8) {{tag> #FpInfor #Dam #DamMp08 #DamMp08Uf3 #DamMp08Uf03 jocs games}} \\ ===== Crides HTTP estàndard ===== Les realitzem mitjançant la [[https://libgdx.com/wiki/networking|llibreria Networking de libGDX]] que ja ve integrada al propi //framework//. ==== Exercicis ==== Fes un [[https://stackoverflow.com/questions/33062574/how-to-properly-implement-a-dialog-box-using-libgdx|projecte libGDX amb Dialog com el de l'exemple]]. Implementa una crida HTTP a alguna web quan premem un botó del Dialog. \\ ===== WebSockets ===== ==== Configuració ==== Podem seguir la [[https://github.com/MrStahlfelge/gdx-websockets|doc oficial del plugin de WebSockets per a libGDX]] però amb un canvi, posant ''api'' enlloc de ''compile''. També caldrà la definició de la variable wsVersion a ''build.gradle'': allprojects { ext { wsVersion = '1.9.10.3' } --> Arxius de configuració (Gradle, etc.)# Per tal que funcionin les comunicacions en Android cal activar els permisos adequats: Si volem compilar versió HTML: I el més important, ''build.gradle'' general del projecte (n'hi ha d'altres dins de cada plataforma): allprojects { ext { wsVersion = '1.9.10.3' } repositories { maven { url "https://jitpack.io" } } } project(":desktop") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:common:$wsVersion" } } project(":android") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:common:$wsVersion" } } project(":html") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:core:$wsVersion:sources" implementation "com.github.MrStahlfelge.gdx-websockets:html:$wsVersion" implementation "com.github.MrStahlfelge.gdx-websockets:html:$wsVersion:sources" } } project(":core") { dependencies { implementation "com.github.MrStahlfelge.gdx-websockets:core:$wsVersion" } } <-- També cal que inicialitzem els WebSockets als arxius ''AndroidLauncher'', ''iOSLauncher'' i ''DesktopLauncher'', just abans de la creació del Launcher: CommonWebSockets.initiate(); \\ ==== Ús en app libGDX ==== * Per connectar-nos al servidor des de l'app libGDX ho fem al constructor o funció de inicialització ''create()''. * Podem enviar des de qualsevol part del codi inclús des del ''render'' tot i que convé no abusar. * Per rebre dades del servidor ho fem mitjançant l'objecte ''WebSocketListener''. import com.github.czyzby.websocket.WebSocketListener; import com.github.czyzby.websocket.WebSocket; class GameScreen extends Screen { WebSocket socket; String address = "localhost"; int port = 8888; // constructor de l'objecte Screen public GameScreen { if( Gdx.app.getType()== Application.ApplicationType.Android ) // en Android el host és accessible per 10.0.2.2 address = "10.0.2.2"; socket = WebSockets.newSocket(WebSockets.toWebSocketUrl(address, port)); socket.setSendGracefully(false); socket.addListener((WebSocketListener) new MyWSListener()); socket.connect(); socket.send("Enviar dades"); } // Es poden enviar dades al render() en tems real! // Millor no fer-ho a cada frame per no saturar el server // ni ralentitzar el joc public void render() { if( stateTime-lastSend > 1.0f ) { lastSend = stateTime; socket.send("Enviar dades"); } } // COMUNICACIONS (rebuda de missatges) ///////////////////////////////////////////// class MyWSListener implements WebSocketListener { @Override public boolean onOpen(WebSocket webSocket) { System.out.println("Opening..."); return false; } @Override public boolean onClose(WebSocket webSocket, int closeCode, String reason) { System.out.println("Closing..."); return false; } @Override public boolean onMessage(WebSocket webSocket, String packet) { System.out.println("Message:"); return false; } @Override public boolean onMessage(WebSocket webSocket, byte[] packet) { System.out.println("Message:"); return false; } @Override public boolean onError(WebSocket webSocket, Throwable error) { System.out.println("ERROR:"+error.toString()); return false; } } } \\ ==== Servidor WebSockets en NodeJS ==== Podem emprar la [[https://www.npmjs.com/package/ws|llibreria WS per a NodeJS]]. Ens caldrà crear un projecte NodeJS i instal·lar les llibreries: $ mkdir ws1 $ cd ws1 $ npm init $ npm install http ws Afegeix ''index.js'' i posa'l en marxa: $ node index.js --> Exemple 1: WebSockets simple# const { createServer } = require('http'); const { WebSocketServer } = require('ws'); const server = createServer(); const wss = new WebSocketServer({ server }); wss.on('connection', function connection(ws) { console.log("Nova connexió."); ws.send('something'); ws.on('error', console.error); ws.on('message', function message(data) { console.log('received: %s', data); }); ws.on('close', function close() { console.log("Tancant connexió.") }) }); server.listen( 8888, function() { console.log("listening..."); }); <-- -->Exemple 2: WebSockets + HTTP# const express = require('express') const http = require('http'); const WebSocket = require('ws') const app = express() const httpServer = http.createServer(app); const wss = new WebSocket.Server({ server: httpServer }) const { v4: uuidv4 } = require('uuid') const port = 8888 var clients = {} // HTTP ROUTES app.use(express.static('public')) app.get('/',root); // WS client connections wss.on('connection', function connection(ws) { var userid = uuidv4(); console.log('Nova connexió: '+userid); clients[userid] = { "id":userid, "ws":ws, pos:{} }; ws.send("Benvingut id="+userid); // TODO: crear totems i actualitzar ws.on('close', function close() { delete clients[userid]; // TODO: esborrar totems? }) ws.on('error', console.error); ws.on('message', function message(data) { try { // exemple per descoficar JSON var posData = JSON.parse(data); posData.id = userid; console.log('Pos data: %s', JSON.stringify(posData)); // retransmetem posició a tothom broadcast(JSON.stringify(posData)); } catch (e) { console.log("ERROR descodificant pos: "+e) } }); }); // SERVER START httpServer.listen(port, appListen) function appListen () { console.log(`Example app listening on: http://localhost:${port}`) } // HTTP ///////////////////////////////// async function root(req,res) { res.send("IETI Game WebSocket Server"); } // WS : Web Sockets ///////////////////////////////// async function broadcast (obj) { for( var id in clients ) { var client = clients[id]; //if (client.readyState === WebSocket.OPEN) { var messageAsString = JSON.stringify(obj) client.ws.send(obj); //} } } <-- \\ ==== Exercicis ==== Implementa el servidor NodeJS indicat. Afegeix la llibreria de WebSockets al teu joc libGDX i fes que envii la posició del nostre personatge 1 cop per segon. Assegura't que funciona comprovant que el servidor mostra el missatge de posicionament del personatge a la seva consola. \\ ===== JSON and Jackson ===== Per treballar amb comunicacions és probable que us calgui codificar i descodificar JSON. Una opció és la [[https://www.arquitecturajava.com/java-json-con-jackson/|llibreria Jackson de Java]], que podem afegir al projecte amb Gradle: project(":core") { dependencies { implementation 'com.fasterxml.jackson.core:jackson-core:2.10.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.10.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.1' } } \\