The ServiceModio
Service Module provides access to the Mod.io. With this service module modding support is added to a game.
The service has the unique identifier Mod.io
.
Support for this service is provided in the DragonScript Script Module using the class ServiceModio. This service implements the following interfaces:
The BaseGameApp class automatically creates the ServiceModio service instance if the following values are present in the configuration.ptree.xml
file:
Tag | Value |
---|---|
modio.gameid | Game identifier |
modio.appkey | AppKey |
The values for all these tags can be found on the game admin page.
An example configuration.ptree.xml
file looks like his:
<?xml version='1.0' encoding='UTF-8'?> <parameterTree> <boolean path='defaultEnableGI'>true</boolean> <boolean path='defaultEnableAuralization'>true</boolean> <!-- Mod.io --> <string path='modio.gameid'>1234</string> <string path='modio.appkey'>1234567890abcdef12345678</string> </parameterTree>
If the provided initialization data is incorrect creating the ServiceModio service fails. This does not fail starting the application. Wait until all the services have finished initializing:
func void initGame() // super call starts automatic initialize services super.initGame() // add listener called once all services have finished initialize, no matter if successfull or not getBaseGameServices().setActionInitialized(BlockActionListener.new(block ActionEvent event // services have initialized launchApplication() end)) // you can now do other things in the mean time like showing startup logos or videos end
You can then check first if the ServiceModio service has been created using:
if getBaseGameServices().getModio() != null // ServiceModio is ready to be used end
The ServiceModio service can be manually created either by using the new
constructor on the ServiceModio class using ServiceModio.InitParameters setup with the initialization data or by calling initModio()
on BaseGameApp. Using initModio
is preferred if manual creation is required.
The service module provides user management support. Supported is logging in, logging, retrieving user information and retrieving user resources.
The user can log in using different methods. The loginUser()
call accepts a ServiceUserCredentials instance. Depending on the set parameters one of the possible login methods is used.
If in the credentials
parameter the member authToken
is set logging in is done using the authentication token from one of the supported Authentication Provider
Service modules. For this to work you have to call getAuthToken()
on the target ServiceUser to asynchronously retrieve an authentication token. Once received in the listener assign the token to the authToken
member as-is.
The service module provides modding support. This is done by adding virtual file system containers
during the game engine launch process populated with all active modifications.
Modifications can be either unsubscribed
, subscribed
or disabled
.
Unsubscribed modifications are not installed.
To install a modification call subscribeMod()
using the modification identifier.
Once subscribed a call to activateMods()
activates all subscribed and enabled modifications. These changes apply immediately. If the modification contains script changes you have to also warm-restart the game scripts using Engine.restart(). Warm restarting restarts the game but otherwise keeps the game engine loaded. Calling modHasMatchingFiles()
helps to determine what files a modification contains to figure out what reloading or restarting actions are required.
Modifications can be temporarily disabled using setModDisabled()
. Disabled modifications are still subscribed to but they will not be activated during activateMods()
calls.
Installed modifications can be examined using getActiveMods()
(which mods are active right now), getSubscribedMods()
(which mods has the user subscribed to) and getSystemMods()
(all modifications installed on the system no matter by which user).
Modifications can be browsed by calling listAllMods()
. This lists modifications page wise and thus needs multiple consecutive calls to browse through large lists.
Modifications can also be rated by the user using submitModRating()
. Ratings can be revoked by calling revokeModRating()
.
Offensive modifications can be reported using reportMod()
.
Modifications can be purchased using purchaseMod()
if monetization is enabled for the game.
Subscribed modifications are downloaded and installed in the background. If you do not want this to happen, for example to not disturb a running game, you can call setPauseModManagement()
to temporarily disable modification management. Background handling can only potentially cause problems if modifications are updated. Installing a modification does not cause problems since modifications need to be activated before they affect the game content.
To make modding even easier for you a full set of UI panels and windows has been added. See MMPanelModManagement, MMWindowModManagement and the linked UI classes.
You only have to show an instance of MMWindowModManagement to provide all the functionalities outlined above. To alter the look of the window and panels add matching designers to your guitheme. See the modmanagement.guitheme.xml for the designers provided by the game engine. The respective UI class API documentation also contains the supported designers.
To support script modding you have to do a bit more. An example you can find in DEMoCap.
Basically it is recommended to require modifications to subclass a Modification
class of sorts provided by your game which is located in a specific namespace. This can look something like this:
namespace MyGame pin Dragengine.Scenery /** Base class for modifications creating specific script objects. */ class Modification func new() end /** Create element classes and add them to element class list. */ func void createAndAddElementClasses(GameApp app) end /** Initialize game. */ func void initGame(GameApp app) end /** Launch application. */ func void launchApplication(GameApp app) end // the above are some example functions. add more depending on what modifications can modify in your game end
You can then use introspection to find all active modifications by checking for which class in this specific namespace subclasses your Modification
class. Code for doing this typically looks like this:
namespace MyGame class MyGameApp extends BaseGameApp public var Array modifications public func new() modifications = Array.new() findModifications() end /** Find modificaitons. */ private func void findModifications() var Class c, ns try ns = Introspection.Class.new("MyGame.Modifications") catch Exception e return end var int i, count = ns.getClassCount() var Modification mod for i = 0 to count c = ns.getClass(i) if c.getClassType() != Class.CLASS continue end try mod = c.getFunction("new", Array.new()).call(null, Array.new()) cast Modification if mod == null throw ENullPointer.new("constructor returned object") end modifications.add(mod) catch Exception e Engine.log("Failed loading modification class " + c.getName()) throw e end end end end
There are other possibilities to do this. For example you can also require modifications to provide an parameter tree file (.ptree.xml) in a specific directory. This file could then contain the fully qualified class name of the modification besides other information. Using introspection you can then load this class similar to the example above.