====== 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'
}
}
\\