Shell Buildscript for Kongregate Deploy – Teil 3 / 3
Lest im 1. Teil, welche Vorbereitungen unser Script durchführt und im 2. Teil erfahrt ihr, wie die Sourcedateien konkateniert und minimiert werden.
Hier im 3. Teil geht es um die zu erstellende .html Datei. Diese ist in allen Spielen auf Kongregate (externer Link) der erste Einstiegspunkt. Hier wird auch die passende .js source Datei geladen, damit das Spiel richtig loslegen kann. Ihr erfahrt heute
- Wie die dev .html aufgebaut ist
- Wie die release .html aussieht
- Wie ich die release .html anhand meines scriptes erstelle
Ich habe mich enstschieden für jeden Build eine neue .js Datei sampt Buildnummer zu erstellen, weil ich öfters mit gecachten .js Dateien zu tun hatte. Das hat vom CDN (Content Delivery Network) bis zum Browser beim Spieler gereicht. Insbesondere letztes habe ich bei häufigen Updates anderer Spiele gesehen. Der Browsercache hat noch die alte Source datei im Speicher, braucht aber schon die neue Version. Klar kann man den Browsercache löschen – jeder Entwickler sollte wissen, wie das geht – aber das ist in meinen Augen schon zu viel, was man da den Usern zumutet. Denen sollte es so einfach wie möglich gemacht werden.
Ich verwende als CDN das AWS S3 von Amazon. Vielleicht schreibe ich in einem späteren Blog Eintrag mehr dazu. Bis jetzt hatte ich damit nur einmal Probleme, als der S3 nicht verfügbar war. Leider habe ich aber dazu keine Life-Daten. Das ganze ist schon eine Weile her.
Bevor mit Topic beginnen, kurz ein Wort zum Data-Tracking. Das habe ich bei mir mit eingebaut, aber werde hier nicht darauf eingehen. Stellt euch einfach vor, ihr habt ein Datensippet (z.B. von google), das ihr in die HTML einbindet.
Die dev HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> [TITLE OF YOUR PROJECT] </title> </head> <body style="background-color:#000;padding:0;margin:0;" onload="startGame()"> <!--ON_DEPLOY_NEXT_LINE_REPLACE_WITH_CANVAS--> <canvas id="canvas" width="800" height="600"></canvas> <!--ON_DEPLOY_INCLUDE_MINJS--> <!--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_START --> ... <script src="js/game.js"></script> ... <!--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_END --> <!--ON_DEPLOY_INCLUDE_KONGAPI--> <!--ON_DEPLOY_INCLUDE_GOOGLEANALYTICS--> </body> </html>
Ich setze hier voraus, dass ihr wißt wie eine .html Datei aufgebaut ist, daher lenke ich euer Augenmerk besonders auf die markierten Kommentarzeilen. Diese Zeilen steuern, wie das Buildscript unsere index.html verarbeiten soll soll. In meiner Programmier-Phylosophie gilt die Prämisse für andere Programmierer den Source zu schreiben. Die Zeilen sollten daher auch beschreiben, was passiert.
<!--ON_DEPLOY_NEXT_LINE_REPLACE_WITH_CANVAS-->
Die nächste Zeile wird gelöscht, und dafür die im Buildscript festgelegten Angaben genommen.
<!--ON_DEPLOY_INCLUDE_MINJS-->
An dieser Stelle soll die finale .js Datein eingebunden werden.
<!--ON_DEPLOY_INCLUDE_KONGAPI-->
<!--ON_DEPLOY_INCLUDE_GOOGLEANALYTICS-->
Binden die Kong-API bzw. die GoogleAnalytcs an dieser Stelle ein.
Besonderes Aukenmert gilt folgendem Snippet:
<!--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_START --> ... <script src="js/Game/BalanceVersionFunctions.js"></script> ... <!--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_END -->
Alle Zeilen in der dev index.html zwischen diesen Anweisungen werden gelöscht und nicht in die release index.html übernammen. Um keine Probleme im Deploy zu bekommen, muss der öffnende Block auch wieder geschlossen werden. Diese Vorgehen hat sich als vorteilhaft herausgestellt, da ich meist mit vielen .js Files arbeite und die in der Entwicklung alle direkt in die index.html einbinde. Final will ich das natürlich nicht haben, das wären viel zu viel calls und damit unnötige Daten, die verschickt werden müssten.
Die release HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> [TITLE OF YOUR PROJECT] </title> </head> <body style="background-color:#000;padding:0;margin:0;" onload="startGame()"> <canvas id="canvas" width="800" height="600"></canvas> <script src="game_20181217_122453270_572.min.js"></script> <script src="https://ssl.kongcdn.com/javascripts/kongregate_api.js"></script> ... google analytics goes here ... </body> </html>
So kann eine release index.html aussehen. Ich habe hier in den Zeilen 11 bis 13 auf meine google analytics angaben verzichtet. Die sind zwar public, ich will aber nicht das da jeder meine Daten übernimmt. 😉
Ihr könnt am Timestamp 20181217_122453270_572 erkennen, wann ich einen Deploy für mein Spiel hatte. Wer in den ersten beiden Teilen aufgepasst hat, der weiß, dass
- dies der 572. Build ist
- am 17.12.2018
- um 12:24h 53,327″ ertellt wurde.
Blog und Build wurden also am gleichen Tag erstellt. 😉
Das Buildscript
Eines muss ich vorweg schicken. Das Sript arbeitet an dieser Stelle mit States. Diese sind unter der CURCOMMAND gespeichert und je nach State wird sich das Script anders verhalten. Das ist aber super simpel, wenn ihr es einmal verstanden habt.
ACHTUNG!! Ich arbeite hier aber mit einem Script, daher wundert euch nicht über die vielen if-else-Anweisungen, die ihr gleich sehen werdet. Klar. In einer Programmierumgebung würde ich hier das State-Pattern verwenden. 😉
Vorbereitung
set CURCOMMAND=READ_LINE set INDEX_HTML_TEMP=%DEPLOY_FOLDER%\index_temp.html
echo. 2>%INDEX_HTML_TEMP%
Als erstes bereitet unser Script den Prozess vor. Es soll die nächste Zeile unserer dev index.html gelesen werden und die Ausgabe in eine index_temp.html passieren, die wir auch gleich als leere Datei erzeugen.
Grundgerüst
for /F "tokens=*" %%A in (%DEPLOY_FOLDER%\index.html) do ( set LINE=%%~nA
... Check the States here ...
) del %DEPLOY_FOLDER%\index.html ren %INDEX_HTML_TEMP% index.html
Jetzt wird jede Zeile der index.html (also die dev html) gelesen. Den Inhalt weise ich hier der variable LINE (Zeile 2) zu. Abschließend wird die dev html gelöschen und die temporär erzeugte .html Datei zur neuen index.html und damit zur release version.
States
Jetzt gehen wir aber ein wenig tiefer in die States aber vorher will ich den kompletten inhalt der for-Schleife zeigen.
if "!CURCOMMAND!"=="READ_LINE" ( if "!LINE:~1,28!"=="--ON_DEPLOY_NEXT_LINE_DELETE" ( set CURCOMMAND=SKIP_LINE ) else if "!LINE:~1,25!"=="--ON_DEPLOY_INCLUDE_MINJS" ( set CURCOMMAND=READ_LINE @echo ^<script src="%MIN_JSFILE_NAME%"^>^</script^> >> %INDEX_HTML_TEMP% ) else if "!LINE:~1,27!"=="--ON_DEPLOY_INCLUDE_KONGAPI" ( set CURCOMMAND=READ_LINE @echo ^<script src="https://ssl.kongcdn.com/javascripts/kongregate_api.js"^>^</script^> >> %INDEX_HTML_TEMP% ) else if "!LINE:~1,41!"=="--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_START" ( set CURCOMMAND=BLOCK_DELETE ) else if "!LINE:~1,41!"=="--ON_DEPLOY_NEXT_LINE_REPLACE_WITH_CANVAS" ( set CURCOMMAND=SKIP_LINE @echo ^<canvas id="canvas" width="%CANVAS_WIDTH%" height="%CANVAS_HEIGHT%"^>^</canvas^> >> %INDEX_HTML_TEMP% ) else ( setlocal DisableDelayedExpansion @echo %%A^ >> %INDEX_HTML_TEMP% endlocal ) ) else if "!CURCOMMAND!"=="BLOCK_DELETE" ( if "!LINE:~1,39!"=="--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_END" ( set CURCOMMAND=READ_LINE ) ) else ( set CURCOMMAND=READ_LINE )
Grundlage ist die äußere If-else if-else Bedingung. Im If-Block werden (fast) alle States verarbeitet. Dieser reicht von Zeile 6-23.
Der else if-Block sorgt dafür, dass der State BLOCK_DELETE (Zeile 24), bei dem mehrere Zeilen en bloc gelöscht werden, wieder verlassen werden kann.
Der else-Block in Zeile 28 sorgt dafür, dass die nächste Zeile wieder korrekt eingelesen werden kann, nachdem alle anderen States abgearbeitet wurden.
States verarbeiten
Wenn ein State verarbeitet wird, dann passieren zwei Dinge:
- Setzte den neuen State
- Verarbeite den aktuellen State
Verarbeiten heißt bei mir im Script, dass eine Ausgabe je nach Vorgabe geschieht oder, dass nichts passiert. Daher steht z.B. in Zeile 6 beim if nur der neue State. Zu tief werde ich hier nicht gehen, denn ich gehe davon aus, dass das meiste bekannt ist. Aber ein paar Kleinigkeiten durchaus:
"!LINE:~1,28!"
Diese Zeile sorgt dafür, dass die eingelesene Zeile ab dem 1. Zeichen (0. wird abgeschnitten) 28 Zeichen einliest.
EnableDelayedExpansion
sorgt dafür, dass das Ausrufezeichen in meinen String gelöscht wird, daher muss ich in Zeile 20 kurz dafür sorgen, dass die Delayes Expansion ausgeschaltet wird, um den kompletten String einzulesen und ausgeben zu können. Genau dieses Problem sorg auch dafür, dass ich im Einlesen der Zeilen aus der index.html das erste Zeichen nicht mit nehme. Ich hätte sonst so etwas:
<--ON_DEPLOY_INCLUDE_KONGAPI-->
Das aber wird zu Problemen mit der index.html führen. Dabei ist Validität das kleinste Problem. Also denkt daran, die extenssion aus und wieder anzuschalten.
BLOCK_DELETE State
... ) else if "!LINE:~1,41!"=="--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_START" ( set CURCOMMAND=BLOCK_DELETE ) ...
Interessant ist noch der BLOCK_DELETE State. Bei diesem sorge ich nur dafür, dass der State gesetzt wird. Im Script xplizit gefragt, ob dieser gesetzt ist. Das ist auch notwendig, denn in diesem State soll ja auch nichts gemacht werden.
Erst wenn der Command zum verlassen des States gefunden wird, ändert das Script wieder das Parsing-Verhalten.
... ) else if "!CURCOMMAND!"=="BLOCK_DELETE" ( if "!LINE:~1,39!"=="--ON_DEPLOY_BLOCK_DELETE_NEXT_LINES_END" ( set CURCOMMAND=READ_LINE ) ) ...
Ab jetzt also kann das Script wieder normal weiter arbeiten.
Learnings
So jetzt wisst ihr, wie ihr ein Script für den Deploy-Prozess einsetzten könnt. Mir ist bewusst, dass ich da einiges verkürzt dargestellt habe. Trotzdem hoffe ich, dass ihr verstanden habt, wie ihr
- ein batch-script verwenden könnt
- .js konkateniert und minimiert
- eine Buildnummer ohne Database extrahiert
- eine release index.html erstellt