{{tag>service modio modding dragonscript}}
[[:start|Start Page]] >> [[:gamedev|Game Development with the Drag[en]gine]] >> [[:gamedev:servicemodules#service_listing|Service Listing]] >> **Mod.io**
====== Mod.io ======
The ''ServiceModio'' Service Module provides access to the [[https://mod.io|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 #@LinkApiDocDEDS2_HTML~classDragengine_1_1Services_1_1ServiceModio,ServiceModio~@#. This service implements the following interfaces:
* #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1Services_1_1User_1_1ServiceUser,ServiceUser~@#
* #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1Services_1_1Stats_1_1ServiceMods,ServiceMods~@#
====== Initialization ======
===== Automatic =====
The #@LinkApiDocDEDS2_HTML~classDragengine_1_1BaseGameApp,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:
true
true
1234
1234567890abcdef12345678
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
===== Manual =====
The ServiceModio service can be manually created either by using the ''new'' constructor on the #@LinkApiDocDEDS2_HTML~classDragengine_1_1Services_1_1ServiceModio,ServiceModio~@# class using #@LinkApiDocDEDS2_HTML~classDragengine_1_1Services_1_1ServiceModio_1_1InitParameters,ServiceModio.InitParameters~@# setup with the initialization data or by calling ''initModio()'' on #@LinkApiDocDEDS2_HTML~classDragengine_1_1BaseGameApp,BaseGameApp~@#. Using ''initModio'' is preferred if manual creation is required.
====== ServiceUser ======
The service module provides user management support. Supported is logging in, logging, retrieving user information and retrieving user resources.
===== Login =====
The user can log in using different methods. The ''loginUser()'' call accepts a #@LinkApiDocDEDS2_HTML~classDragengine_1_1Services_1_1User_1_1ServiceUserCredentials,ServiceUserCredentials~@# instance. Depending on the set parameters one of the possible login methods is used.
==== Authentication Provider ====
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 #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1Services_1_1User_1_1ServiceUser,ServiceUser~@# to asynchronously retrieve an authentication token. Once received in the listener assign the token to the ''authToken'' member as-is.
====== ServiceMods ======
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 #@LinkApiDocDEDS2_HTML~classDragengine_1_1Engine,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.
===== DragonScript UI Classes =====
To make modding even easier for you a full set of UI panels and windows has been added. See #@LinkApiDocDEDS2_HTML~classDragengine_1_1Gui_1_1ModManagement_1_1MMPanelModManagement,MMPanelModManagement~@#, #@LinkApiDocDEDS2_HTML~classDragengine_1_1Gui_1_1ModManagement_1_1MMWindowModManagement,MMWindowModManagement~@# and the linked UI classes.
You only have to show an instance of #@LinkApiDocDEDS2_HTML~classDragengine_1_1Gui_1_1ModManagement_1_1MMWindowModManagement,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 [[https://github.com/LordOfDragons/dragengine/blob/master/src/modules/scripting/dragonscript/data/guithemes/shared/modmanagement.guitheme.xml|modmanagement.guitheme.xml]] for the designers provided by the game engine. The respective UI class API documentation also contains the supported designers.
===== Script Modifications =====
To support script modding you have to do a bit more. An example you can find in [[https://github.com/LordOfDragons/democap/blob/main/data/scripts/Modification.ds|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.