This is a documentation for Board Game Arena: play board games online !
User:SwHawk/Create Modular Code: Difference between revisions
No edit summary |
No edit summary |
||
(17 intermediate revisions by the same user not shown) | |||
Line 8: | Line 8: | ||
Writing this wiki article wouldn't have been possible without the help, work and insight of more experienced BGA developers: | Writing this wiki article wouldn't have been possible without the help, work and insight of more experienced BGA developers: | ||
* Tisaac | * [https://boardgamearena.com/player?id=83846198 Tisaac] (Discord user Tisaac Human Expert) | ||
* bennygui (Discord user) | * [https://boardgamearena.com/player?id=85537631 bennygui] (Discord user bennygui) | ||
* | * [https://boardgamearena.com/player?id=84125723 VictoriaLa] (Discord user VictoriaLa) | ||
* Syarwin and quietmint (for their Welcome To [https://github.com/Syarwin/bga-welcometo repository]) | * Syarwin and [https://boardgamearena.com/player?id=84554161 quietmint] (for their Welcome To [https://github.com/Syarwin/bga-welcometo repository]) | ||
* [https://boardgamearena.com/player?id=84522426 Mogri] (Discord user Mogri) | |||
* Discord user Tug Brice (Ice 9 Games) | |||
== Conventions == | |||
In the code blocks, code additions will be displayed in bold and green, and added comments will be displayed in bold and dark grey so that they stand out from the boilerplate code from a scratch project. Part in red should be aapted to reflect your project's structure. | |||
== On the PHP side == | == On the PHP side == | ||
Line 27: | Line 32: | ||
Basically you create a Trait in th <code>/modules/</code> directory, for instance: | Basically you create a Trait in th <code>/modules/</code> directory, for instance: | ||
< | <nowiki>[[modules/UtiltityFunctionsTrait.php]]</nowiki> | ||
<?php | <span style="color:green">'''<?php''' | ||
namespace YourProjectNamespace\Modules; | '''namespace YourProjectNamespace\Modules;''' | ||
trait UtilityFunctionsTrait { | '''trait UtilityFunctionsTrait {''' | ||
'''public function myFirstUtilityFunction($args)''' | |||
'''{''' | |||
</span><span style="color:dimgrey">'''//Your code here'''</span><span style="color:green"> | |||
'''}''' | |||
'''}'''</span> | |||
Then you can include it in you project's <code>X.game.php</code> file like so: | Then you can include it in you project's <code>X.game.php</code> file like so: | ||
<nowiki>[[X_game.php]]</nowiki> | |||
<?php | <?php | ||
/** | |||
*------ | |||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.game.php | |||
* | |||
* This is the main file for your game logic. | |||
* | |||
* In this PHP file, you are going to defines the rules of the game. | |||
* | * | ||
*/ | |||
'''<span style="color:green">require_once 'modules/UtilityFunctionsTrait.php'; </span><span style="color:dimgrey"> //Here we require the trait to be loaded when the class is loaded</span>''' | |||
<span style="color:green">'''use X\Modules\UtilityFunctionsTrait;'''</span><span style="color:dimgrey">''' //We import the trait to be used directly as UtilityFunctionsTraits,''' | |||
'''//otherwise we would have to use the Fully qualified class name''' | |||
'''//(YourProjectNamespace\Modules\UtilityFunctionsTrait)'''</span> | |||
class X extends Table | |||
{ | |||
//... | |||
////////////////////////////////////////////////////////////////////////////// | |||
//////////// Utility functions | |||
//////////// | |||
/* | |||
In this space, you can put any utility methods useful for your game logic | |||
*/ | |||
<span style="color:green">'''use UtilityFunctionsTrait;'''</span> <span style="color:dimgrey">'''//Here we actually include the trait inside the class, making all the trait's methods''' | |||
'''//available as class methods, keeping their visibility scope (so a private method''' | |||
'''//is still private)'''</span> | |||
} | |||
=== Support classes === | === Support classes === | ||
Line 107: | Line 116: | ||
==== Autoloader function for development ==== | ==== Autoloader function for development ==== | ||
While your project is under development, you usually create, move and remove classes as part of the development process. So you usually need an autoloading function that can handle a lot of flexibility. Basically once you import a class using the <code>use</code> keyword, the autoloading function will determine which file to <code>require</code> using its fully qualified class name, and then <code>require</code>. Such a function would have the following code (borrowed and adapted from [https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md this repository]): | While your project is under development, you usually create, move and remove classes as part of the development process. So you usually need an autoloading function that can handle a lot of flexibility. Basically once you import a class using the <code>use</code> keyword, the autoloading function will determine which file to <code>require</code> using its fully qualified class name, and then <code>require</code>. This function needs to be either defined or included at the top of <code>X_game.php</code>. Such a function would have the following code (borrowed and adapted from [https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md this repository]): | ||
<span style="color:green">'''spl_autoload_register(function ($class) {'''</span> | |||
'''<span style="color:dimgrey">// project-specific namespace prefix</span><span style="color:red">, adapt to your needs</span><span style="color:dimgrey">, remember that you must escape \ characters in double-quoted strings (hence the \\)</span>'''<span style="color:green"> | |||
'''$prefix = '''</span><span style="color:red">'''"SwHawk\\X\\"'''</span><span style="color:green">''';'''</span> | |||
'''<span style="color:dimgrey">// base directory for the namespace prefix, this file would usually reside in the modules/php/includes/ directory, so the base directory for all my classes would be a level higher</span>'''<span style="color:green"> | |||
'''$base_dir = __DIR__ . '''</span><span style="color:red">''' '/../' '''</span><span style="color:green>''';''' | |||
</span>'''<span style="color:dimgrey">// does the class use the namespace prefix?</span>'''<span style="color:green"> | |||
'''$len = strlen($prefix);''' | |||
'''if (strncmp($prefix, $class, $len) !== 0) {''' | |||
</span>'''<span style="color:dimgrey">// no, move to the next registered autoloader</span>'''<span style="color:green"> | |||
'''return;''' | |||
'''}''' | |||
</span>'''<span style="color:dimgrey">// get the relative class name</span>'''<span style="color:green"> | |||
'''$relative_class = substr($class, $len);''' | |||
</span><span style="color:dimgrey">'''// replace the namespace prefix with the base directory, replace namespace''' | |||
'''// separators with directory separators in the relative class name, append''' | |||
'''// with .php'''</span><span style="color:green"> | |||
'''$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';''' | |||
</span>'''<span style="color:dimgrey">// if the file exists, require it</span>'''<span style="color:green"> | |||
'''if (file_exists($file)) {''' | |||
'''require $file;''' | |||
'''}''' | |||
'''});'''</span> | |||
==== Autoloader function for production ==== | ==== Autoloader function for production ==== | ||
Once you're moving your game to production, the autoloader function provided before isn't optimized. Once in production, the directory tree should not be modified, so you can change the earlier autoloader to a classmap based one. Basically, you build an associative array with the fully qualified classes names as keys and the filepath as the value. An example would look like so: | Once you're moving your game to production, the autoloader function provided before isn't optimized. Once in production, the directory tree should not be modified, so you can change the earlier autoloader to a classmap based one. Basically, you build an associative array with the fully qualified classes names as keys and the filepath as the value. An example would look like so: | ||
< | <span style="color:green">'''spl_autoload_register(function ($class) {''' | ||
spl_autoload_register(function ($class) { | '''static $classmap = array(''' | ||
</span>'''<span style="color:red>"SwHawk\\X\\</span><span style="color:green">Factory\\AFactoryClass" => __DIR__ . </span><span style="color:red">"/../</span><span style="color:green">Factory/AFactoryClass.php",</span>'''<span style="color:green"> | |||
'''...''' | |||
''');''' | |||
'''if(array_key_exists($class)) {''' | |||
'''require $classmap[$class];''' | |||
'''} else {''' | |||
'''return;''' | |||
'''}''' | |||
'''}''' | |||
} | |||
This autoloader is better suited to production since it won't use expensive calls like <code>file_exists($filename)</code>. You can write the classmap by yourself, but it can be quite cumbersome, there is some tools like [https://github.com/hanneskod/classtools this project], or [https://github.com/gnugat/nomo-spaco this one], or even scripts detailed in this [https://stackoverflow.com/questions/22761554/how-to-get-all-class-names-inside-a-particular-namespace StackOverflow topic] | This autoloader is better suited to production since it won't use expensive calls like <code>file_exists($filename)</code>. You can write the classmap by yourself, but it can be quite cumbersome, there is some tools like [https://github.com/hanneskod/classtools this project], or [https://github.com/gnugat/nomo-spaco this one], or even scripts detailed in this [https://stackoverflow.com/questions/22761554/how-to-get-all-class-names-inside-a-particular-namespace StackOverflow topic] | ||
Line 163: | Line 170: | ||
=== Write plain javascript === | === Write plain javascript === | ||
==== Relying on the dojo toolkit ==== | |||
Should you wan to write plain javascript, you can easily do so. What you need to do is actually load the file like so in <code>X.js</code>: | Should you wan to write plain javascript, you can easily do so. What you need to do is actually load the file like so in <code>X.js</code>: | ||
< | <nowiki>[[X.js]]</nowiki> | ||
<nowiki>[[X.js]]</nowiki> | /** | ||
/** | *------ | ||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.js | |||
* | |||
* X user interface script | |||
* | |||
* In this file, you are describing the logic of your user interface, in Javascript language. | |||
* | |||
*/ | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
'''<span style="color:green">g_gamethemeurl + "/modules/path/to/firstfile.js",</span> <span style="color:dimgrey>//The first file with your JS code</span>''' | |||
'''<span style="color:green">g_gamethemeurl + "/modules/path/to/secondfile.js",</span> <span style="color:dimgrey>//The second file with another part of your JS code</span>''' | |||
... | |||
], | |||
function (dojo, declare) { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
constructor: function(){ | |||
... | |||
} | |||
According to the dojo toolkit documentation, you will then need to directly access what you defined in the files. | |||
==== Alternative method ==== | |||
This section has mostly been written by [[User:Mogri|Mogri]]. | |||
This approach lets you avoid Dojo and <tt>this</tt> and allows more modern JS. | |||
First, you'll need to a a statement at the beginning of the <code>X.js</code> file: | |||
<nowiki>[[X.js]]</nowiki> | |||
/** | |||
*------ | *------ | ||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | ||
Line 183: | Line 232: | ||
* | * | ||
*/ | */ | ||
'''<span style="color:green">window.X = {};</span> <span style="color:dimgrey">// X = your game, or you can use something more generic</span>''' | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
<span style="color:green">'''g_gamethemeurl + "/modules/whatever.js"'''</span> | |||
], | |||
function (dojo, declare) { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
constructor: function(){ | |||
console.log('X constructor'); | |||
// Here, you can init the global variables of your user interface | |||
// Example: | |||
// this.myGlobalValue = 0; | |||
}, | |||
/* | |||
setup: | |||
This method must set up the game user interface according to current game situation specified | |||
in parameters. | |||
The method is called each time the game interface is displayed to a player, ie: | |||
_ when the game starts | |||
_ when a player refreshes the game page (F5) | |||
"gamedatas" argument contains all datas retrieved by your "getAllDatas" PHP method. | |||
*/ | |||
setup: function( gamedatas ) | |||
{ | |||
'''<span style="color:green">X = Object.assign(this, X);</span> <span style="color:dimgrey">// This will allow you to use X.method instead of this.method inside modules/whatever.js</span>''' | |||
'''<span style="color:dimgrey">// the rest of your setup goes here</span>''' | |||
}, | |||
}); | |||
}); | |||
And then, in your <code>modules/whatever.js</code> file: | |||
<nowiki>[[modules/whatever.js]]</nowiki> | |||
<span style="color:green">'''X = Object.assign(X, {''' | |||
'''MY_CONSTANT: 1,''' | |||
'''myFunction: (arg1, arg2) => {''' | |||
'''X.someFunction(X.MY_CONSTANT);''' | |||
'''return arg1 + arg2;''' | |||
'''},''' | |||
'''someFunction: (x) => _('The answer is: ') + x,''' | |||
'''});'''</span> | |||
Notes on this approach: | |||
</ | *Aside from setup and any framework-override functions, you can use arrow functions | ||
*Always use X instead of <tt>this</tt> | |||
*As usual, you can use any number of modules this way | |||
=== Write an AMD module === | === Write an AMD module === | ||
Line 204: | Line 294: | ||
==== Create a named new class ==== | ==== Create a named new class ==== | ||
<nowiki>[[modules/js/FirstModule.js]]</nowiki> | <nowiki>[[modules/js/FirstModule.js]]</nowiki> | ||
define([ | <span style="color:green>'''define([''' | ||
'''"dojo",</span> <span style="color:dimgrey">//Needed if you want to be able to use dojo methods such as dojo.place, etc.</span>'''<span style="color:green"> | |||
'''"dojo/_base/declare",''' | |||
'''other dependencies...''' | |||
],function(dojo, declare){ | '''],function(dojo, declare){''' | ||
'''return declare("className",</span> <span style="color:dimgrey">/*Parent classes that this class inherits, optional*/</span><span style="color:green">, {''' | |||
</span><span style="color:dimgrey>'''/* static properties */'''</span><span style="color:green"> | |||
'''let property1 = "value;''' | |||
'''let property2 = 5;''' | |||
'''constructor: function (args) {'''</span> | |||
<span style="color:dimgrey>'''/* instance properties */'''</span> | |||
<span style="color:green">'''let property3 = 8;''' | |||
'''let property4 = [ "arrayValue", "arrayValue2" ]; ''' | |||
'''}''' | |||
'''method1: function (args) {'''</span> | |||
<span style="color:dimgrey">'''/* your code here */'''</span> | |||
<span style="color:green">'''}''' | |||
'''...''' | |||
'''});''' | |||
}); | '''});'''</span> | ||
</ | |||
Then in the X.js file, you can use your newly created module like so: | Then in the X.js file, you can use your newly created module like so: | ||
<nowiki>[[X.js]]</nowiki> | |||
<nowiki>[[X.js]]</nowiki> | /** | ||
/** | *------ | ||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.js | |||
* | |||
* X user interface script | |||
* | |||
* In this file, you are describing the logic of your user interface, in Javascript language. | |||
* | |||
*/ | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
<span style="color:green">'''"./modules/js/Firstmodule",'''</span> | |||
], | |||
function (dojo, declare) { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
... | |||
}); | |||
}); | |||
In your code, you can access your module's class and methods by doing so: | In your code, you can access your module's class and methods by doing so: | ||
< | |||
<span style="color:green">'''let object = new className();''' | |||
'''object.method1();''' | |||
'''...'''</span> | |||
</ | |||
This can lead to namespace collisions if you don't choose a namespace correctly, so you might want to take the same approach as described earlier for the PHP files for using a namespace. | This can lead to namespace collisions if you don't choose a namespace correctly, so you might want to take the same approach as described earlier for the PHP files for using a namespace. | ||
Line 272: | Line 360: | ||
==== Create an anonymous class ==== | ==== Create an anonymous class ==== | ||
<nowiki>[[modules/js/SecondModule.js]]</nowiki> | |||
<span style="color:green>'''define([''' | |||
'''"dojo",</span> <span style="color:dimgrey">//Needed if you want to be able to use dojo methods such as dojo.place, etc.</span>'''<span style="color:green"> | |||
'''"dojo/_base/declare",''' | |||
'''other dependencies...''' | |||
'''],function(dojo, declare){''' | |||
'''return declare(</span> <span style="color:dimgrey">/*Optional : parent classes that this class inherits, must be required in the array, and in the define callback arguments*/</span><span style="color:green">, {''' | |||
</span><span style="color:dimgrey>'''/* static properties */'''</span><span style="color:green"> | |||
'''let property1 = "value;''' | |||
'''let property2 = 5;''' | |||
'''constructor: function (args) {'''</span> | |||
<span style="color:dimgrey>'''/* instance properties */'''</span> | |||
<span style="color:green">'''let property3 = 8;''' | |||
'''let property4 = [ "arrayValue", "arrayValue2" ]; ''' | |||
'''}''' | |||
'''method1: function (args) {'''</span> | |||
<span style="color:dimgrey">'''/* your code here */'''</span> | |||
<span style="color:green">'''}''' | |||
'''...''' | |||
'''});''' | |||
'''});'''</span> | |||
Then in the X.js file, you can use your newly created module like so: | |||
<nowiki>[[X.js]]</nowiki> | |||
<nowiki>[[ | /** | ||
define([ | *------ | ||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.js | |||
* | |||
* X user interface script | |||
* | |||
* In this file, you are describing the logic of your user interface, in Javascript language. | |||
* | |||
*/ | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
<span style="color:green">'''"./modules/js/SecondModule",'''</span> <span style="color:dimgrey">'''//Has to be inserted in the third position as we'll''' | |||
}); | '''//need an identifier later, and the other modules don't need it, see code in the preceding subsection.'''</span> | ||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
], | |||
function (dojo, declare'''<span style="color:green">, SecondModule</span> <span style="color:dimgrey">/* insert a reference here that you'll use throughout the JS file for this module */</span>''') { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
... | |||
}); | |||
}); | |||
In your code, you can access your module's class and methods by doing so: | |||
< | <span style="color:green">'''let myObject = new SecondModule(); | ||
'''let someValue = myObject.property3''' | |||
'''myObject.method1();''' | |||
'''...'''</span> | |||
This can lead to namespace collisions if you don't choose a namespace correctly, so you might want to take the same approach as described earlier for the PHP files for using a namespace. | |||
==== Create a singleton object ==== | ==== Create a singleton object ==== | ||
Line 344: | Line 430: | ||
You can also create singleton objects by creating a module this way: | You can also create singleton objects by creating a module this way: | ||
< | |||
[[modules/js/singletonObject.js]] | <nowiki>[[modules/js/singletonObject.js]]</nowiki> | ||
define([ | <span style="color:green">'''define([''' | ||
'''"dojo",</span> <span style="color:dimgrey">//Needed if you want to be able to use dojo methods such as dojo.place, etc.</span>'''<span style="color:green"> | |||
'''...''' | |||
], function (dojo) { | '''], function (dojo) {''' | ||
</span><span style="color:dimgrey">'''// Methods and variables defined in this part will not be available anywhere else than in this file'''</span><span style="color:green"> | |||
'''let privateVariable = "someValue";''' | |||
'''let privateFunction = function (args) {''' | |||
</span><span style="color:dimgrey">'''//some code here'''</span><span style="color:green"> | |||
'''}''' | |||
'''return {''' | |||
'''firstMethod: function (args) {''' | |||
'''privateFunction(args);''' | |||
</span><span style="color:dimgrey">'''//some other code here'''</span><span style="color:green"> | |||
'''}''' | |||
'''};''' | |||
}); | '''});'''</span> | ||
</ | |||
To be used in the <code>X.js</code> file, you must use the same method as in the [[#Create an anonymous class|previous section]]: | To be used in the <code>X.js</code> file, you must use the same method as in the [[#Create an anonymous class|previous section]]: | ||
define([ | <nowiki>[[X.js]]</nowiki> | ||
/** | |||
*------ | |||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
], | * See http://en.boardgamearena.com/#!doc/Studio for more information. | ||
function (dojo, declare, SecondModule /* insert a reference here that you'll use throughout the JS file for this module */, singletonObject) { | * ----- | ||
* | |||
* X.js | |||
* | |||
} | * X user interface script | ||
* | |||
* In this file, you are describing the logic of your user interface, in Javascript language. | |||
* | |||
*/ | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
'''<span style="color:green">"./modules/js/SecondModule",</span> <span style="color:dimgrey">//Has to be inserted in the third position as we'll need an identifier later, and the other modules don't need it</span>''' | |||
'''<span style="color:green">"./modules/js/sigletonObject",</span> <span style="color:dimgrey">//see code in the preceding subsection.</span>''' | |||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
<span style="color:green">'''"./modules/js/Firstmodule",'''</span> | |||
], | |||
function (dojo, declare'''<span style="color:green">, SecondModule</span> <span style="color:dimgrey">/* insert a reference here that you'll use throughout the JS file for this module */</span><span style="color:green">, singletonObject</span> <span style="color:dimgrey">/* same here */</span>''') { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
... | |||
}); | |||
}); | |||
Then you can access your code the same way as described in the [[#Create an anonymous class|previous section]]: | Then you can access your code the same way as described in the [[#Create an anonymous class|previous section]]: | ||
<span style="color:green">'''singletonObject.firstMethod();''' | |||
'''...'''</span> | |||
== Putting it all together == | |||
As you might have gathered reading this, much of your work will rely on the directory structure your project will follow. | |||
=== Recommended directory structure === | |||
This is a recommendation, and in no way are you forced to follow it, but then you may have to adapt some of the code snippets shown below. All filenames should be self-explanatory, although there might be comments for some files. | |||
<pre> | <pre> | ||
X/ | |||
img/ | |||
... | ... | ||
misc/ | |||
... | |||
modules/ | |||
js/ | |||
utility/ | |||
FirstUtilityModule.js (defines a [[#Create a singleton object|singleton object]]) | |||
SecondUtilityModule.js (defines an [[#Create an anonymous class|anonymous class]]) | |||
notifications/ | |||
FirstNotificationModule.js (defines a [[#Create a new named class|class with a name]]) | |||
actions/ | |||
FirstActionModule.js (plain JS code) | |||
php/ | |||
includes/ | |||
PNAutoloader.php (contains your autoloader, PN stands for ProjectName, and should be a trigram if possible) | |||
Other files to be included into files of the project. For instance splits from material.inc.php. | |||
Object/ | |||
ObjectA.php | |||
ObjectB.php | |||
Traits/ | |||
PlayerActions/ | |||
PlayerActionsFunctionsTrait.php | |||
States/ | |||
Actions/ | |||
StateActionsFunctionsTrait.php | |||
Arguments/ | |||
StateArgumentFunctionsTrait.php | |||
Utility/ | |||
FirstUtilityFunctionsTrait.php | |||
SecondUtilityFunctionsTrait.php | |||
</pre> | </pre> | ||
== | This directory tree is quite detailed, you're free to make arrangements with it to suit your project's requirements. | ||
'''TODO''' | |||
=== Choosing a namespace prefix === | |||
==== PHP ==== | |||
As stated earlier, in order to avoid collisions when importing your PHP Classes and Traits, you should define a namespace prefix. We'll use <code>Username\ProjectName</code> throughout the remainder of this article. | |||
==== JS ==== | |||
As we'll create a named class, we should also use a namespace prefix for the JS module. We'll use <code>username.projectname</code> throughout the remainder of this article | |||
=== Write the autoloader and include it in the game logic === | |||
Even though you may have a lot less PHP classes than stated here, you should really write an autoloader function, and since it's recommended for the production environment, you should proceed to write a [[#Autoloader function for production|classmap autoloader]] when your classes are pretty much stabilised. | |||
Then you need to include it in your X_game.php like so: | |||
<nowiki>[[X_game.php]]</nowiki> | |||
<?php | |||
/** | |||
*------ | |||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.game.php | |||
* | |||
* This is the main file for your game logic. | |||
* | |||
* In this PHP file, you are going to defines the rules of the game. | |||
* | |||
*/ | |||
'''<span style="color:green">require_once 'modules/php/includes/PNAutoloader.php';</span>''' | |||
'''<span style="color:dimgrey">//Here you'll write the use statements to import your classes and traits using the autoloader</span>''' | |||
class X extends Table | |||
{ | |||
'''<span style="color:dimgrey">//Here you can write the use statements to import your traits into the class, or put them in the relevant sections of the file</span>''' | |||
=== Write your Javascript modules and include them === | |||
On the JS side of the code, you have multiple alternatives to choose from (see the [[#On the JS side|JS section]]), but all will require you to modify the <code>define</code> statement at the beginning of the <code>X.js</code> file. According to the directory structure shown above, the file would look like so: | |||
<nowiki>[[X.js]]</nowiki> | |||
<nowiki>[[X.js]]</nowiki> | |||
/** | |||
*------ | |||
* BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> | |||
* X implementation : © <Your name here> <Your email address here> | |||
* | |||
* This code has been produced on the BGA studio platform for use on http://boardgamearena.com. | |||
* See http://en.boardgamearena.com/#!doc/Studio for more information. | |||
* ----- | |||
* | |||
* X.js | |||
* | |||
* X user interface script | |||
* | |||
* In this file, you are describing the logic of your user interface, in Javascript language. | |||
* | |||
*/ | |||
define([ | |||
"dojo","dojo/_base/declare", | |||
<span style="color:green">'''"./modules/js/utility/FirstUtilityModule",''' | |||
'''"./modules/js/utility/SecondUtilityModule",'''</span> | |||
"ebg/core/gamegui", | |||
"ebg/counter", | |||
<span style="color:green">'''"./modules/js/actions/FirstActionModule.js",''' | |||
'''"./modules/js/notifications/FirstNotificationModule",</span> | |||
], | |||
function (dojo, declare, '''<span style="color:green">FirstUtilityModule, SecondUtilityModule</span>''') { | |||
return declare("bgagame.X", ebg.core.gamegui, { | |||
constructor: function(){ | |||
console.log('X constructor'); | |||
// Here, you can init the global variables of your user interface | |||
// Example: | |||
// this.myGlobalValue = 0; | |||
}, | |||
/* | |||
setup: | |||
This method must set up the game user interface according to current game situation specified | |||
in parameters. | |||
The method is called each time the game interface is displayed to a player, ie: | |||
_ when the game starts | |||
_ when a player refreshes the game page (F5) | |||
"gamedatas" argument contains all datas retrieved by your "getAllDatas" PHP method. | |||
*/ | |||
setup: function( gamedatas ) | |||
{ | |||
<span style="color:green">'''FirstUtilityModule.myMethod();''' | |||
'''SecondUtilityModule.anotherMethod(gamedatas);'''</span> | |||
}, | |||
... | |||
/////////////////////////////////////////////////// | |||
//// Reaction to cometD notifications | |||
/* | |||
setupNotifications: | |||
In this method, you associate each of your game notifications with your local method to handle it. | |||
Note: game notification names correspond to "notifyAllPlayers" and "notifyPlayer" calls in | |||
your progressevolutiontechnologyswh.game.php file. | |||
*/ | |||
setupNotifications: function() | |||
{ | |||
console.log( 'notifications subscriptions setup' ); | |||
// TODO: here, associate your game notifications with local methods | |||
// Example 1: standard notification handling | |||
// dojo.subscribe( 'cardPlayed', this, "notif_cardPlayed" ); | |||
// Example 2: standard notification handling + tell the user interface to wait | |||
// during 3 seconds after calling the method in order to let the players | |||
// see what is happening in the game. | |||
// dojo.subscribe( 'cardPlayed', this, "notif_cardPlayed" ); | |||
// this.notifqueue.setSynchronous( 'cardPlayed', 3000 ); | |||
// | |||
// TODO: check module integration with dojo.subscribe | |||
}, | |||
}); | |||
}); | |||
=== See it in a real project === | |||
This method has been partially or totally applied on the following projects: | |||
* [https://gitlab.swhawk.eu/silver/progressevolutiontechnologybga Progress: Evolution of Technology] by [[User:SwHawk|SwHawk]] | |||
If you have successfully applied this method in your project, feel free to add you project to the list by editing this section |
Latest revision as of 22:53, 29 November 2022
This page will cover steps you can take as a BGA developer to make your code more modular and better organized. The way the framework is implemented makes the game logic and the interface logic files very monolithic. While this is not a problem in itself, even simple to moderately complex game may require this files to be over 5k lines of code. Thus you can quickly become lost in all the functions you need to declare, where they are declared, and where they are called.
But there are steps that you can follow to take advantage of PHP's Object Oriented Code and the dojo toolkit's AMD loader to organize your code in separate files. Let's see what's available:
Acknowledgements
Writing this wiki article wouldn't have been possible without the help, work and insight of more experienced BGA developers:
- Tisaac (Discord user Tisaac Human Expert)
- bennygui (Discord user bennygui)
- VictoriaLa (Discord user VictoriaLa)
- Syarwin and quietmint (for their Welcome To repository)
- Mogri (Discord user Mogri)
- Discord user Tug Brice (Ice 9 Games)
Conventions
In the code blocks, code additions will be displayed in bold and green, and added comments will be displayed in bold and dark grey so that they stand out from the boilerplate code from a scratch project. Part in red should be aapted to reflect your project's structure.
On the PHP side
include
vs require
Throughout this section, I'll make heavy use of PHP's require_once
control structure. Why am I using require(_once)
instead of include_once
? Because if an error exists in a PHP file that you try to include
, only a warning will be thrown, but the script will continue in spite of the error. Using require
if the included file has an error, the script dies when the inclusion takes places, throwing a fatal error (E_COMPILE_ERROR
).
That way, I'm sure the classes, traits or interfaces I'm including are syntax error free.
Game logic methods
The way the framework is implemented, your game logic is gathered inside a class (YourProjectName
) extending the Table
class. You define your methods there so that they may be called from other parts of the framework (as long as they're public methods obviously).
But you can take advantage of PHP Traits. In a nutshell, Traits are a collection of methods that can be reused in different classes, to avoid code duplication. Here we're not going to reuse them anywhere else than in the game class, but this little trick allows us to put methods definitions in other files, and in so doing to avoid monolithic code.
Basically you create a Trait in th /modules/
directory, for instance:
[[modules/UtiltityFunctionsTrait.php]] <?php namespace YourProjectNamespace\Modules; trait UtilityFunctionsTrait { public function myFirstUtilityFunction($args) { //Your code here } }
Then you can include it in you project's X.game.php
file like so:
[[X_game.php]] <?php /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.game.php * * This is the main file for your game logic. * * In this PHP file, you are going to defines the rules of the game. * */ require_once 'modules/UtilityFunctionsTrait.php'; //Here we require the trait to be loaded when the class is loaded use X\Modules\UtilityFunctionsTrait; //We import the trait to be used directly as UtilityFunctionsTraits, //otherwise we would have to use the Fully qualified class name //(YourProjectNamespace\Modules\UtilityFunctionsTrait) class X extends Table { //... ////////////////////////////////////////////////////////////////////////////// //////////// Utility functions //////////// /* In this space, you can put any utility methods useful for your game logic */ use UtilityFunctionsTrait; //Here we actually include the trait inside the class, making all the trait's methods //available as class methods, keeping their visibility scope (so a private method //is still private) }
Support classes
Basically, the same as traits, you put your classes definitions into files that live under the /modules/
directory (don't forget to namespace your classes), and then require them at the top of X_game.php
, and voilà, you're done
Adding autoloader support
Coding standards
Even for simple games, you might end up having a dozen of traits and support classes. You can require them all in your X_game.php
. Or you can use an autoloader, which will load the necessary classes and traits at runtime. There are two PSRs dealing with autloading standards (namely PSR-0 which is marked as deprecated and PSR-4) which state that your namespaces should be a reflection of the directory structure of your project.
Basically, when creating namespaces, you choose a namespace prefix. For example, you could use your BGA handle and the project name as a namespace prefix (would result in \SwHawk\X
in my case), and then the rest of the namespace would be the directory the file is in. I usually group classes and traits by the function they have in my project, so my /modules/
directory looks like that:
modules/ php/ Factory/ //Holds my factory classes AFactoryClass.php //The namespace for this file would be \SwHawk\X\Factory, and the fully qualified class name would be \SwHawk\X\Factory\AFactoryClass ... Object/ //Holds my support classes ObjectA.php //The namespace for this file would be \SwHawk\X\Object, and the fully qualified class name would be \SwHawk\X\Object\ObjectA ... Traits/ //Holds my traits UtilityFunctionsTrait.php //The namespace for this file would be \SwHawk\X\Traits, and the fully qualified class name would be \SwHawk\X\Traits\UtilityFunctionsTrait ... ... ...
Autoloader function for development
While your project is under development, you usually create, move and remove classes as part of the development process. So you usually need an autoloading function that can handle a lot of flexibility. Basically once you import a class using the use
keyword, the autoloading function will determine which file to require
using its fully qualified class name, and then require
. This function needs to be either defined or included at the top of X_game.php
. Such a function would have the following code (borrowed and adapted from this repository):
spl_autoload_register(function ($class) { // project-specific namespace prefix, adapt to your needs, remember that you must escape \ characters in double-quoted strings (hence the \\) $prefix = "SwHawk\\X\\"; // base directory for the namespace prefix, this file would usually reside in the modules/php/includes/ directory, so the base directory for all my classes would be a level higher $base_dir = __DIR__ . '/../' ; // does the class use the namespace prefix? $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { // no, move to the next registered autoloader return; } // get the relative class name $relative_class = substr($class, $len); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // if the file exists, require it if (file_exists($file)) { require $file; } });
Autoloader function for production
Once you're moving your game to production, the autoloader function provided before isn't optimized. Once in production, the directory tree should not be modified, so you can change the earlier autoloader to a classmap based one. Basically, you build an associative array with the fully qualified classes names as keys and the filepath as the value. An example would look like so:
spl_autoload_register(function ($class) { static $classmap = array( "SwHawk\\X\\Factory\\AFactoryClass" => __DIR__ . "/../Factory/AFactoryClass.php", ... ); if(array_key_exists($class)) { require $classmap[$class]; } else { return; } }
This autoloader is better suited to production since it won't use expensive calls like file_exists($filename)
. You can write the classmap by yourself, but it can be quite cumbersome, there is some tools like this project, or this one, or even scripts detailed in this StackOverflow topic
On the JS side
BGA uses the dojo toolkit framework for most of its javascript code. Dojo comes with its own way of loading modules, which you can read about in more detail here and here. To put it simply, there are two ways you can go about this
Write plain javascript
Relying on the dojo toolkit
Should you wan to write plain javascript, you can easily do so. What you need to do is actually load the file like so in X.js
:
[[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", g_gamethemeurl + "/modules/path/to/firstfile.js", //The first file with your JS code g_gamethemeurl + "/modules/path/to/secondfile.js", //The second file with another part of your JS code ... ], function (dojo, declare) { return declare("bgagame.X", ebg.core.gamegui, { constructor: function(){ ... }
According to the dojo toolkit documentation, you will then need to directly access what you defined in the files.
Alternative method
This section has mostly been written by Mogri.
This approach lets you avoid Dojo and this and allows more modern JS.
First, you'll need to a a statement at the beginning of the X.js
file:
[[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ window.X = {}; // X = your game, or you can use something more generic define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", g_gamethemeurl + "/modules/whatever.js" ], function (dojo, declare) { return declare("bgagame.X", ebg.core.gamegui, { constructor: function(){ console.log('X constructor'); // Here, you can init the global variables of your user interface // Example: // this.myGlobalValue = 0; }, /* setup: This method must set up the game user interface according to current game situation specified in parameters. The method is called each time the game interface is displayed to a player, ie: _ when the game starts _ when a player refreshes the game page (F5) "gamedatas" argument contains all datas retrieved by your "getAllDatas" PHP method. */ setup: function( gamedatas ) { X = Object.assign(this, X); // This will allow you to use X.method instead of this.method inside modules/whatever.js // the rest of your setup goes here }, }); });
And then, in your modules/whatever.js
file:
[[modules/whatever.js]]
X = Object.assign(X, {
MY_CONSTANT: 1,
myFunction: (arg1, arg2) => {
X.someFunction(X.MY_CONSTANT);
return arg1 + arg2;
},
someFunction: (x) => _('The answer is: ') + x,
});
Notes on this approach:
- Aside from setup and any framework-override functions, you can use arrow functions
- Always use X instead of this
- As usual, you can use any number of modules this way
Write an AMD module
You can take advantage of the toolkit's module system to declare dependencies between the differents files you'll be writing (modules in the toolkit's language). To be identified as modules, your Javascript files will need to have the following structure:
Create a named new class
[[modules/js/FirstModule.js]] define([ "dojo", //Needed if you want to be able to use dojo methods such as dojo.place, etc. "dojo/_base/declare", other dependencies... ],function(dojo, declare){ return declare("className", /*Parent classes that this class inherits, optional*/, { /* static properties */ let property1 = "value; let property2 = 5; constructor: function (args) { /* instance properties */ let property3 = 8; let property4 = [ "arrayValue", "arrayValue2" ]; } method1: function (args) { /* your code here */ } ... }); });
Then in the X.js file, you can use your newly created module like so:
[[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "./modules/js/Firstmodule", ], function (dojo, declare) { return declare("bgagame.X", ebg.core.gamegui, { ... }); });
In your code, you can access your module's class and methods by doing so:
let object = new className();
object.method1();
...
This can lead to namespace collisions if you don't choose a namespace correctly, so you might want to take the same approach as described earlier for the PHP files for using a namespace.
Create an anonymous class
[[modules/js/SecondModule.js]] define([ "dojo", //Needed if you want to be able to use dojo methods such as dojo.place, etc. "dojo/_base/declare", other dependencies... ],function(dojo, declare){ return declare( /*Optional : parent classes that this class inherits, must be required in the array, and in the define callback arguments*/, { /* static properties */ let property1 = "value; let property2 = 5; constructor: function (args) { /* instance properties */ let property3 = 8; let property4 = [ "arrayValue", "arrayValue2" ]; } method1: function (args) { /* your code here */ } ... }); });
Then in the X.js file, you can use your newly created module like so:
[[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ define([ "dojo","dojo/_base/declare", "./modules/js/SecondModule", //Has to be inserted in the third position as we'll //need an identifier later, and the other modules don't need it, see code in the preceding subsection. "ebg/core/gamegui", "ebg/counter", ], function (dojo, declare, SecondModule /* insert a reference here that you'll use throughout the JS file for this module */) { return declare("bgagame.X", ebg.core.gamegui, { ... }); });
In your code, you can access your module's class and methods by doing so:
let myObject = new SecondModule();
let someValue = myObject.property3
myObject.method1();
...
This can lead to namespace collisions if you don't choose a namespace correctly, so you might want to take the same approach as described earlier for the PHP files for using a namespace.
Create a singleton object
You can also create singleton objects by creating a module this way:
[[modules/js/singletonObject.js]] define([ "dojo", //Needed if you want to be able to use dojo methods such as dojo.place, etc. ... ], function (dojo) { // Methods and variables defined in this part will not be available anywhere else than in this file let privateVariable = "someValue"; let privateFunction = function (args) { //some code here } return { firstMethod: function (args) { privateFunction(args); //some other code here } }; });
To be used in the X.js
file, you must use the same method as in the previous section:
[[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ define([ "dojo","dojo/_base/declare", "./modules/js/SecondModule", //Has to be inserted in the third position as we'll need an identifier later, and the other modules don't need it "./modules/js/sigletonObject", //see code in the preceding subsection. "ebg/core/gamegui", "ebg/counter", "./modules/js/Firstmodule", ], function (dojo, declare, SecondModule /* insert a reference here that you'll use throughout the JS file for this module */, singletonObject /* same here */) { return declare("bgagame.X", ebg.core.gamegui, { ... }); });
Then you can access your code the same way as described in the previous section:
singletonObject.firstMethod();
...
Putting it all together
As you might have gathered reading this, much of your work will rely on the directory structure your project will follow.
Recommended directory structure
This is a recommendation, and in no way are you forced to follow it, but then you may have to adapt some of the code snippets shown below. All filenames should be self-explanatory, although there might be comments for some files.
X/ img/ ... misc/ ... modules/ js/ utility/ FirstUtilityModule.js (defines a [[#Create a singleton object|singleton object]]) SecondUtilityModule.js (defines an [[#Create an anonymous class|anonymous class]]) notifications/ FirstNotificationModule.js (defines a [[#Create a new named class|class with a name]]) actions/ FirstActionModule.js (plain JS code) php/ includes/ PNAutoloader.php (contains your autoloader, PN stands for ProjectName, and should be a trigram if possible) Other files to be included into files of the project. For instance splits from material.inc.php. Object/ ObjectA.php ObjectB.php Traits/ PlayerActions/ PlayerActionsFunctionsTrait.php States/ Actions/ StateActionsFunctionsTrait.php Arguments/ StateArgumentFunctionsTrait.php Utility/ FirstUtilityFunctionsTrait.php SecondUtilityFunctionsTrait.php
This directory tree is quite detailed, you're free to make arrangements with it to suit your project's requirements.
Choosing a namespace prefix
PHP
As stated earlier, in order to avoid collisions when importing your PHP Classes and Traits, you should define a namespace prefix. We'll use Username\ProjectName
throughout the remainder of this article.
JS
As we'll create a named class, we should also use a namespace prefix for the JS module. We'll use username.projectname
throughout the remainder of this article
Write the autoloader and include it in the game logic
Even though you may have a lot less PHP classes than stated here, you should really write an autoloader function, and since it's recommended for the production environment, you should proceed to write a classmap autoloader when your classes are pretty much stabilised.
Then you need to include it in your X_game.php like so:
[[X_game.php]] <?php /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.game.php * * This is the main file for your game logic. * * In this PHP file, you are going to defines the rules of the game. * */ require_once 'modules/php/includes/PNAutoloader.php'; //Here you'll write the use statements to import your classes and traits using the autoloader class X extends Table { //Here you can write the use statements to import your traits into the class, or put them in the relevant sections of the file
Write your Javascript modules and include them
On the JS side of the code, you have multiple alternatives to choose from (see the JS section), but all will require you to modify the define
statement at the beginning of the X.js
file. According to the directory structure shown above, the file would look like so:
[[X.js]] [[X.js]] /** *------ * BGA framework: © Gregory Isabelli <gisabelli@boardgamearena.com> & Emmanuel Colin <ecolin@boardgamearena.com> * X implementation : © <Your name here> <Your email address here> * * This code has been produced on the BGA studio platform for use on http://boardgamearena.com. * See http://en.boardgamearena.com/#!doc/Studio for more information. * ----- * * X.js * * X user interface script * * In this file, you are describing the logic of your user interface, in Javascript language. * */ define([ "dojo","dojo/_base/declare", "./modules/js/utility/FirstUtilityModule", "./modules/js/utility/SecondUtilityModule", "ebg/core/gamegui", "ebg/counter", "./modules/js/actions/FirstActionModule.js", "./modules/js/notifications/FirstNotificationModule", ], function (dojo, declare, FirstUtilityModule, SecondUtilityModule) { return declare("bgagame.X", ebg.core.gamegui, { constructor: function(){ console.log('X constructor'); // Here, you can init the global variables of your user interface // Example: // this.myGlobalValue = 0; }, /* setup: This method must set up the game user interface according to current game situation specified in parameters. The method is called each time the game interface is displayed to a player, ie: _ when the game starts _ when a player refreshes the game page (F5) "gamedatas" argument contains all datas retrieved by your "getAllDatas" PHP method. */ setup: function( gamedatas ) { FirstUtilityModule.myMethod(); SecondUtilityModule.anotherMethod(gamedatas); }, ... /////////////////////////////////////////////////// //// Reaction to cometD notifications /* setupNotifications: In this method, you associate each of your game notifications with your local method to handle it. Note: game notification names correspond to "notifyAllPlayers" and "notifyPlayer" calls in your progressevolutiontechnologyswh.game.php file. */ setupNotifications: function() { console.log( 'notifications subscriptions setup' ); // TODO: here, associate your game notifications with local methods // Example 1: standard notification handling // dojo.subscribe( 'cardPlayed', this, "notif_cardPlayed" ); // Example 2: standard notification handling + tell the user interface to wait // during 3 seconds after calling the method in order to let the players // see what is happening in the game. // dojo.subscribe( 'cardPlayed', this, "notif_cardPlayed" ); // this.notifqueue.setSynchronous( 'cardPlayed', 3000 ); // // TODO: check module integration with dojo.subscribe }, }); });
See it in a real project
This method has been partially or totally applied on the following projects:
If you have successfully applied this method in your project, feel free to add you project to the list by editing this section