Einleitung
In diesem Tutorial lernt man, wie man eine grafische Oberfläche in der UserApps verwenden kann, um z. B. grafische Spiele für Knuddels zu programmieren. Man sollte schon einmal etwas mit HTML und JavaScript gemacht haben, um folgen zu können.
Der Server
Der Server-Code der UserApp steht in der main.js. Wenn man nur in der main.js programmiert, läuft der ganze Code der UserApp ausschließlich auf den Servern von Knuddels.
Damit kann man zwar schon viel machen, aber für mehr Interaktionsmöglichkeiten braucht man eine grafische Oberfläche.
Der Client
Der Code für den Client befindet sich im www-Unterordner der UserApp. Den Einstieg bildet eine HTML-Datei, die man nennen kann, wie man will. Dort kann man dann auch JavaScript-Dateien einbinden, um Code auf dem Gerät des Besuchers auszuführen.
Die Struktur der UserApp könnte dann so aussehen:
KnuddelJumper/ www/ game.html game.js player.png enemy.png main.js
Die HTML-Datei kann dann z.B. so eine Struktur haben:
<html> <head> <script src="game.js"/> </head> <body> <button>Hier klicken</button> </body> </html>
Während der Entwicklung wird man allerdings oft Änderungen an dem Script machen. Da Knuddels das Script zwischenspeichert, wenn man es einfach mit dem <script>-Tag einbindet, werden die Änderungen oft nicht übernommen. Um Knuddels dazu zu zwingen immer die aktuellste Version des Scripts zu laden, muss man das Knuddels stattdessen mit Client.includeJS() mitteilen:
<html> <head> <script> Client.includeJS("game.js"); </script> </head> <body> <button>Hier klicken</button> </body> </html>
Kommunikation zwischen Server und Client
AppContent senden
Die HTML-Datei und der restliche Inhalt des www-Unterordners wird AppContent genannt. Das ist die grafische Oberfäche der UserApp.
Der AppContent erscheint aber nicht automatisch bei dem Besucher des Channels! Man muss den AppContent erst an den Besucher (den Client) senden.
In der Regel macht man das direkt wenn der Besucher den Channel betritt, also in App.onUserJoined:
var App = (new function() { let htmlFile = new HTMLFile("game.html"); let appContent = AppContent.overlayContent(htmlFile, 640, 480); this.onUserJoined = function(user) { user.sendAppContent(appContent); } }());
Der AppContent wird so nur einmal von der App geladen und dann einfach an jeden Besucher mit User.sendAppContent() gesendet.
Hinweis: new HTMLFile() sucht die angegebene Datei im www-Unterordner.
Damit ist dieses Tutorial eigentlich fertig: Die Besucher sehen jetzt eine grafische Oberfläche, wenn sie den Channel betreten und man kann wie auf Webseiten Scripts einbinden. Ein paar wichtige Hinweise kommen aber noch!
Der AppContent kann verschiedene ViewModes annehmen, je nachdem welche Methode man verwendet:
- Overlay mit AppContent.overlayContent()
- Popup mit AppContent.popupContent()
- Headerbar mit AppContent.headerbarContent()
Tipp: Nicht alle Geräte können jeden ViewMode anzeigen. Daher ist es gute Praxis, wenn man mit User.canSendAppContent() entscheidet, welchen AppContent man sendet:
Beispiel:
var App = (new function() { let htmlFile = new HTMLFile("game.html"); let overlay = AppContent.overlayContent(htmlFile, 640, 480); let popup = AppContent.popupContent(htmlFile, 640, 480); let headerbar = AppContent.headerbarContent(htmlFile, 480); this.onUserJoined = function(user) { if (user.canSendAppContent(overlay)) { user.sendAppContent(overlay); } else if (user.canSendAppContent(popup)) { user.sendAppContent(popup); } else if (user.canSendAppContent(headerbar)) { user.sendAppContent(headerbar); } else { user.sendPrivateMessage("Die UserApp kann auf deinem Gerät leider nicht angezeigt werden."); } } }());
Events senden
Der Code auf dem Server und der Code auf dem Client laufen jetzt praktisch unabhängig voneinander. Meistens ist es aber hilfreich, wenn Server und Client miteinander kommunizieren können.
Client → Server
Beispiel: Wenn das Mitglied auf der grafischen Oberfläche einen Knopf drückt, soll der AppBot eine öffentliche Nachricht in den Channel schreiben.
Man kann aber nur von der main.js (also vom Server aus) Chat-Nachrichten schicken. BotUser.sendPublicMessage() funktioniert im Client nicht.
Der Client muss also dem Server mitteilen, dass das Mitglied einen Button gedrückt hat. Das ist mit Client.sendEvent() möglich:
Client.sendEvent("buttonPressed", {});
Dabei können noch zusätzliche Daten in Form eines JavaScript-Objekts mitgegeben werden.
Der Server kann dann in der main.js in App.onEventReceived() reagieren:
var App = (new function() { //... this.onEventReceived = function(user, type, data, appContentSession) { if (type == "buttonPressed") { let bot = KnuddelsServer.getDefaultBotUser(); bot.sendPublicMessage(user.getProfileLink()+" hat den Knopf gedrückt!"); } } }());
Server → alle Clients
Beispiel: In der main.js wird eine zufällige Zahl generiert. Diese Zahl soll an alle Besucher des Channels geschickt werden, sodass diese dann in der HTML-UI angezeigt werden kann.
Mit AppContent.sendEvent() kann man ein Event an alle Besucher schicken, bei denen dieser AppContent aktiv ist:
let randomNumber = Math.floor(10 * Math.random()); appContent.sendEvent("showNumber", {randomNumber: number});
Im Client kann man mit Client.addEventListener() auf die Events vom Server reagieren:
Client.addEventListener("showNumber", function(event) { let number = event.data.number; document.open(); document.write("Die magische Zahl heißt: "+number); document.close() });
Server → ein bestimmter Client
Beispiel: Man wählt einen Besucher zufällig aus und möchte, dass sich bei diesem Besucher die Farbe der HTML-UI ändert.
Dazu muss man vom Server aus (also in der main.js) diesem Client ein Event senden.
Man kann aber nicht einfach User.sendEvent() aufrufen (diesen Befehl gibt es nicht). Eine UserApp kann nämlich verschiedene ViewModes auf einmal verwenden (sogenannte AppContentSessions). Man muss der API also sagen, an welche AppContentSession man das Event senden möchte.
Wenn man weiß, welchen ViewMode der Besucher verwendet (z.B. weil man sowieso immer nur ein Popup sendet), kann man einfach mit dem Befehl User.getAppContentSession die AppContentSession erhalten und dann das Event senden. Beispiel:
let appContentSession = user.getAppContentSession(AppViewMode.Popup); appContentSession.sendEvent("changeColor", {red:255, green:0, blue:255});
Wenn der Client allerdings gleichzeitig mehrere aktive AppContentSessions haben könnte (z.B. eine Headerbar und ein Popup), kann man eine Liste aller aktiven AppContentSessions eines Besuchers mit User.getAppContentSessions() erhalten. Beispiel:
let appContentSessions = user.getAppContentSessions(); for (let i = 0; i < appContentSessions.length; i++) { let appContentSession = appContentSessions[i]; appContentSession.sendEvent("changeColor", {red:255, green:0, blue:255}); }
Auch hier kann man im Client wieder mit Client.addEventListener() auf die Events reagieren.
Limits
Für die Daten, die man mit den Events zwischen Client und Server sendet, gibt es gewisse Limits. Diese Limits werden hier beschrieben.