feature(subsystems): extract DiscordRPCSubSystem (#4233)
* docs(subsystems): add Readme for DiscordRPC subsystem * feature(subsystems): add separate configuration for subsystems and add it to classpath * feature(subsystems): add subsystems to dist * feature(subsystems): add README for subsystems * chore(subsystems): Use logger.info for subsystems. use common.gradle instead publish.gradle * feature(subsystems): re-integrate afk subsystem handling in discord submodule Co-authored-by: Tobias Nett <skaldarnar@googlemail.com>develop
parent
4de0df937b
commit
04572ef4e7
|
@ -85,7 +85,6 @@ dependencies {
|
|||
implementation group: 'io.netty', name: 'netty', version: '3.10.5.Final'
|
||||
implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.6.1'
|
||||
implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.2'
|
||||
|
||||
// Javax for protobuf due to @Generated - needed on Java 9 or newer Javas
|
||||
// TODO: Can likely replace with protobuf Gradle task and omit the generated source files instead
|
||||
implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
|
||||
|
@ -133,9 +132,6 @@ dependencies {
|
|||
}
|
||||
implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '4.10'
|
||||
|
||||
// Discord RPC
|
||||
api 'com.jagrosh:DiscordIPC:0.4'
|
||||
|
||||
// Our developed libs
|
||||
api group: 'org.terasology', name: 'gestalt-module', version: '5.1.5'
|
||||
api group: 'org.terasology', name: 'gestalt-util', version: '5.1.5'
|
||||
|
|
|
@ -16,15 +16,18 @@
|
|||
|
||||
package org.terasology.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.terasology.engine.subsystem.rpc.DiscordRPCSubSystem;
|
||||
import org.terasology.nui.Color;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.settings.CieCamColors;
|
||||
import org.terasology.utilities.random.FastRandom;
|
||||
import org.terasology.utilities.random.Random;
|
||||
import org.terasology.utilities.subscribables.AbstractSubscribable;
|
||||
|
||||
public class PlayerConfig {
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerConfig extends AbstractSubscribable {
|
||||
|
||||
public static final String DISCORD_PRESENCE = "DISCORD_PRESENCE";
|
||||
public static final String PLAYER_NAME = "PLAYER_NAME";
|
||||
|
||||
private static final float DEFAULT_PLAYER_HEIGHT = 1.8f;
|
||||
|
||||
|
@ -49,7 +52,9 @@ public class PlayerConfig {
|
|||
}
|
||||
|
||||
public void setName(String name) {
|
||||
String oldName = this.name;
|
||||
this.name = name;
|
||||
propertyChangeSupport.firePropertyChange(PLAYER_NAME, oldName, name);
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
|
@ -87,14 +92,9 @@ public class PlayerConfig {
|
|||
}
|
||||
|
||||
public void setDiscordPresence(boolean discordPresence) {
|
||||
boolean oldValue = this.discordPresence;
|
||||
this.discordPresence = discordPresence;
|
||||
if (DiscordRPCSubSystem.isEnabled() != discordPresence) {
|
||||
if (discordPresence) {
|
||||
DiscordRPCSubSystem.enable();
|
||||
} else {
|
||||
DiscordRPCSubSystem.disable();
|
||||
}
|
||||
}
|
||||
propertyChangeSupport.firePropertyChange(DISCORD_PRESENCE, oldValue, discordPresence);
|
||||
}
|
||||
|
||||
public boolean isDiscordPresence() {
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 MovingBlocks
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.entitySystem.systems;
|
||||
|
||||
import org.terasology.engine.subsystem.rpc.DiscordRPCSubSystem;
|
||||
import org.terasology.game.Game;
|
||||
import org.terasology.network.NetworkMode;
|
||||
import org.terasology.network.NetworkSystem;
|
||||
import org.terasology.registry.In;
|
||||
|
||||
/**
|
||||
* It's a system that runs when a single player or multi player game has been started to process some stuff
|
||||
* throw the {@link DiscordRPCSubSystem}.
|
||||
*
|
||||
* @see DiscordRPCSubSystem
|
||||
*/
|
||||
@RegisterSystem(RegisterMode.CLIENT)
|
||||
public class DiscordRPCSystem extends BaseComponentSystem {
|
||||
|
||||
@In
|
||||
private Game game;
|
||||
|
||||
@In
|
||||
private NetworkSystem networkSystem;
|
||||
|
||||
public String getGame() {
|
||||
NetworkMode networkMode = networkSystem.getMode();
|
||||
String mode = "Playing Online";
|
||||
if (networkMode == NetworkMode.DEDICATED_SERVER) {
|
||||
mode = "Hosting | " + game.getName();
|
||||
} else if (networkMode == NetworkMode.NONE) {
|
||||
mode = "Solo | " + game.getName();
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preBegin() {
|
||||
DiscordRPCSubSystem.setState(getGame(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBegin() {
|
||||
DiscordRPCSubSystem.setState(getGame(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
DiscordRPCSubSystem.setState("In Lobby");
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ package org.terasology.logic.afk;
|
|||
|
||||
import org.terasology.assets.ResourceUrn;
|
||||
import org.terasology.engine.Time;
|
||||
import org.terasology.engine.subsystem.rpc.DiscordRPCSubSystem;
|
||||
import org.terasology.entitySystem.entity.EntityRef;
|
||||
import org.terasology.entitySystem.event.EventPriority;
|
||||
import org.terasology.entitySystem.event.ReceiveEvent;
|
||||
|
@ -79,11 +78,9 @@ public class AfkClientSystem extends BaseComponentSystem {
|
|||
if (component.afk) {
|
||||
nuiManager.pushScreen(SCREEN_URL, AfkScreen.class).setAfkClientSystem(this);
|
||||
nuiManager.closeScreen(CONSOLE_SCREEN_URL);
|
||||
enableDiscord();
|
||||
console.addMessage("[AFK] You are AFK now!");
|
||||
} else {
|
||||
nuiManager.closeScreen(SCREEN_URL);
|
||||
disableDiscord();
|
||||
console.addMessage("[AFK] You are no longer AFK!");
|
||||
}
|
||||
AfkRequest request = new AfkRequest(entity, component.afk);
|
||||
|
@ -99,7 +96,6 @@ public class AfkClientSystem extends BaseComponentSystem {
|
|||
if (!component.afk) {
|
||||
component.afk = true;
|
||||
nuiManager.pushScreen(SCREEN_URL, AfkScreen.class).setAfkClientSystem(this);
|
||||
enableDiscord();
|
||||
AfkRequest request = new AfkRequest(entity, true);
|
||||
entity.send(request);
|
||||
}
|
||||
|
@ -154,29 +150,11 @@ public class AfkClientSystem extends BaseComponentSystem {
|
|||
return lastActive;
|
||||
}
|
||||
|
||||
private String getGame() {
|
||||
NetworkMode networkMode = networkSystem.getMode();
|
||||
String mode = "Playing Online";
|
||||
if (networkMode == NetworkMode.DEDICATED_SERVER) {
|
||||
mode = "Hosting | " + game.getName();
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
private void updateActive() {
|
||||
lastActive = time.getGameTimeInMs();
|
||||
}
|
||||
|
||||
private void enableDiscord() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
DiscordRPCSubSystem.setState("Idle", true);
|
||||
}
|
||||
|
||||
private void disableDiscord() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
DiscordRPCSubSystem.setState(getGame(), true);
|
||||
}
|
||||
|
||||
private boolean disable() {
|
||||
EntityRef clientEntity = localPlayer.getClientEntity();
|
||||
AfkComponent component = clientEntity.getComponent(AfkComponent.class);
|
||||
|
@ -186,7 +164,6 @@ public class AfkClientSystem extends BaseComponentSystem {
|
|||
clientEntity.addOrSaveComponent(component);
|
||||
AfkRequest request = new AfkRequest(clientEntity, false);
|
||||
clientEntity.send(request);
|
||||
disableDiscord();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.google.common.base.Charsets;
|
|||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.terasology.assets.ResourceUrn;
|
||||
|
|
|
@ -20,12 +20,18 @@ import com.google.common.base.Functions;
|
|||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.math.DoubleMath;
|
||||
import org.terasology.assets.ResourceUrn;
|
||||
import org.terasology.config.Config;
|
||||
import org.terasology.context.Context;
|
||||
import org.terasology.engine.subsystem.rpc.DiscordRPCSubSystem;
|
||||
import org.terasology.engine.SimpleUri;
|
||||
import org.terasology.i18n.TranslationProject;
|
||||
import org.terasology.i18n.TranslationSystem;
|
||||
import org.terasology.identity.storageServiceClient.StorageServiceWorker;
|
||||
import org.terasology.identity.storageServiceClient.StorageServiceWorkerStatus;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.StorageServiceLoginPopup;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.ThreeButtonPopup;
|
||||
import org.terasology.nui.Color;
|
||||
import org.terasology.nui.WidgetUtil;
|
||||
import org.terasology.nui.databinding.DefaultBinding;
|
||||
import org.terasology.nui.databinding.ReadOnlyBinding;
|
||||
import org.terasology.nui.widgets.UIButton;
|
||||
import org.terasology.nui.widgets.UICheckbox;
|
||||
import org.terasology.nui.widgets.UIDropdownScrollable;
|
||||
|
@ -33,27 +39,20 @@ import org.terasology.nui.widgets.UIImage;
|
|||
import org.terasology.nui.widgets.UILabel;
|
||||
import org.terasology.nui.widgets.UISlider;
|
||||
import org.terasology.nui.widgets.UIText;
|
||||
import org.terasology.utilities.Assets;
|
||||
import org.terasology.assets.ResourceUrn;
|
||||
import org.terasology.config.Config;
|
||||
import org.terasology.engine.SimpleUri;
|
||||
import org.terasology.i18n.TranslationProject;
|
||||
import org.terasology.i18n.TranslationSystem;
|
||||
import org.terasology.registry.In;
|
||||
import org.terasology.rendering.assets.texture.Texture;
|
||||
import org.terasology.rendering.assets.texture.TextureUtil;
|
||||
import org.terasology.nui.Color;
|
||||
import org.terasology.rendering.nui.CoreScreenLayer;
|
||||
import org.terasology.nui.WidgetUtil;
|
||||
import org.terasology.rendering.nui.animation.MenuAnimationSystems;
|
||||
import org.terasology.nui.databinding.DefaultBinding;
|
||||
import org.terasology.nui.databinding.ReadOnlyBinding;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.StorageServiceLoginPopup;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.ThreeButtonPopup;
|
||||
import org.terasology.utilities.Assets;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.terasology.identity.storageServiceClient.StatusMessageTranslator.getLocalizedButtonMessage;
|
||||
import static org.terasology.identity.storageServiceClient.StatusMessageTranslator.getLocalizedStatusMessage;
|
||||
|
@ -326,7 +325,6 @@ public class PlayerSettingsScreen extends CoreScreenLayer {
|
|||
if (nametext != null) {
|
||||
config.getPlayer().setName(nametext.getText().trim());
|
||||
config.getPlayer().setHasEnteredUsername(true);
|
||||
DiscordRPCSubSystem.updateState();
|
||||
}
|
||||
if (!config.getSystem().getLocale().equals(language.getSelection())) {
|
||||
config.getSystem().setLocale(language.getSelection());
|
||||
|
|
|
@ -56,7 +56,7 @@ val rootDirDist = File(rootDir, "build/distributions")
|
|||
|
||||
// Inherited props
|
||||
val dirNatives: String by rootProject.extra
|
||||
val distsDirectory: DirectoryProperty by project;
|
||||
val distsDirectory: DirectoryProperty by project
|
||||
|
||||
// Read environment variables, including variables passed by jenkins continuous integration server
|
||||
val env: MutableMap<String, String> = System.getenv()!!
|
||||
|
@ -88,6 +88,7 @@ group = "org.terasology.facades"
|
|||
dependencies {
|
||||
implementation(project(":engine"))
|
||||
implementation(group = "org.reflections", name = "reflections", version = "0.9.10")
|
||||
implementation(project(":subsystems:DiscordRPC"))
|
||||
|
||||
// TODO: Consider whether we can move the CR dependency back here from the engine, where it is referenced from the main menu
|
||||
implementation(group = "org.terasology.crashreporter", name = "cr-terasology", version = "4.1.0")
|
||||
|
@ -171,7 +172,7 @@ tasks.register<Sync>("setupServerModules") {
|
|||
description =
|
||||
"""Parses "extraModules" - a comma-separated list of modules and puts them into $localServerDataPath"""
|
||||
|
||||
val extraModules: String? by project;
|
||||
val extraModules: String? by project
|
||||
extraModules?.let {
|
||||
// Grab modules from Artifactory - cheats by declaring them as dependencies
|
||||
it.splitToSequence(",").forEach {
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.terasology.engine.subsystem.common.ConfigurationSubsystem;
|
|||
import org.terasology.engine.subsystem.common.ThreadManager;
|
||||
import org.terasology.engine.subsystem.common.hibernation.HibernationSubsystem;
|
||||
import org.terasology.engine.subsystem.config.BindsSubsystem;
|
||||
import org.terasology.engine.subsystem.discordrpc.DiscordRPCSubSystem;
|
||||
import org.terasology.engine.subsystem.headless.HeadlessAudio;
|
||||
import org.terasology.engine.subsystem.headless.HeadlessGraphics;
|
||||
import org.terasology.engine.subsystem.headless.HeadlessInput;
|
||||
|
@ -29,7 +30,6 @@ import org.terasology.engine.subsystem.lwjgl.LwjglGraphics;
|
|||
import org.terasology.engine.subsystem.lwjgl.LwjglInput;
|
||||
import org.terasology.engine.subsystem.lwjgl.LwjglTimer;
|
||||
import org.terasology.engine.subsystem.openvr.OpenVRInput;
|
||||
import org.terasology.engine.subsystem.rpc.DiscordRPCSubSystem;
|
||||
import org.terasology.game.GameManifest;
|
||||
import org.terasology.network.NetworkMode;
|
||||
import org.terasology.rendering.nui.layers.mainMenu.savedGames.GameInfo;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# DiscordRPC Subsystem
|
||||
|
||||
The Discord RPC subsystem (rich presence via IPC) allows to set and update the status message in Discord for Terasology.
|
||||
When this system is active and the player has the Discord desktop client open the user status will be shown as "Playing Terasology" (or something like that).
|
||||
|
||||
The subsystem uses https://github.com/jagrosh/DiscordIPC (`com.jagrosh:DiscordIPC`) to communicate with the Discord client.
|
||||
|
||||
## Requirements
|
||||
|
||||
This subsystem only has an effect if the user has the [Discord Desktop client](https://discord.com/) and is logged in.
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/config/gradle/common.gradle")
|
||||
|
||||
dependencies {
|
||||
implementation(project(":engine"))
|
||||
api("com.jagrosh:DiscordIPC:0.4")
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.terasology.engine.subsystem.rpc;
|
||||
package org.terasology.engine.subsystem.discordrpc;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
|
@ -25,10 +25,13 @@ import com.jagrosh.discordipc.entities.pipe.WindowsPipe;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.terasology.config.Config;
|
||||
import org.terasology.config.PlayerConfig;
|
||||
import org.terasology.context.Context;
|
||||
import org.terasology.engine.GameEngine;
|
||||
import org.terasology.engine.subsystem.EngineSubsystem;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +40,7 @@ import java.time.OffsetDateTime;
|
|||
*
|
||||
* @see EngineSubsystem
|
||||
*/
|
||||
public class DiscordRPCSubSystem implements EngineSubsystem, IPCListener, Runnable {
|
||||
public class DiscordRPCSubSystem implements EngineSubsystem, IPCListener, Runnable, PropertyChangeListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DiscordRPCSubSystem.class);
|
||||
private static final long DISCORD_APP_CLIENT_ID = 515274721080639504L;
|
||||
|
@ -193,6 +196,7 @@ public class DiscordRPCSubSystem implements EngineSubsystem, IPCListener, Runnab
|
|||
@Override
|
||||
public void postInitialise(Context context) {
|
||||
config = context.get(Config.class);
|
||||
config.getPlayer().subscribe(this);
|
||||
setState("In Lobby");
|
||||
}
|
||||
|
||||
|
@ -295,4 +299,20 @@ public class DiscordRPCSubSystem implements EngineSubsystem, IPCListener, Runnab
|
|||
return getInstance().enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(PlayerConfig.DISCORD_PRESENCE)) {
|
||||
boolean discordPresence = (boolean) evt.getNewValue();
|
||||
if (isEnabled() != discordPresence) {
|
||||
if (discordPresence) {
|
||||
enable();
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (evt.getPropertyName().equals(PlayerConfig.PLAYER_NAME)) {
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package org.terasology.engine.subsystem.discordrpc;
|
||||
|
||||
import org.terasology.entitySystem.entity.EntityRef;
|
||||
import org.terasology.entitySystem.event.ReceiveEvent;
|
||||
import org.terasology.entitySystem.systems.BaseComponentSystem;
|
||||
import org.terasology.entitySystem.systems.RegisterMode;
|
||||
import org.terasology.entitySystem.systems.RegisterSystem;
|
||||
import org.terasology.game.Game;
|
||||
import org.terasology.logic.afk.AfkEvent;
|
||||
import org.terasology.logic.players.LocalPlayer;
|
||||
import org.terasology.network.NetworkMode;
|
||||
import org.terasology.network.NetworkSystem;
|
||||
import org.terasology.registry.In;
|
||||
|
||||
/**
|
||||
* It's a system that runs when a single player or multi player game has been started to process some stuff throw the
|
||||
* {@link DiscordRPCSubSystem}.
|
||||
*
|
||||
* @see DiscordRPCSubSystem
|
||||
*/
|
||||
@RegisterSystem(RegisterMode.CLIENT)
|
||||
public class DiscordRPCSystem extends BaseComponentSystem {
|
||||
|
||||
@In
|
||||
private Game game;
|
||||
|
||||
@In
|
||||
private LocalPlayer player;
|
||||
|
||||
@In
|
||||
private NetworkSystem networkSystem;
|
||||
|
||||
public String getGame() {
|
||||
NetworkMode networkMode = networkSystem.getMode();
|
||||
String mode = "Playing Online";
|
||||
if (networkMode == NetworkMode.DEDICATED_SERVER) {
|
||||
mode = "Hosting | " + game.getName();
|
||||
} else if (networkMode == NetworkMode.NONE) {
|
||||
mode = "Solo | " + game.getName();
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
@ReceiveEvent
|
||||
public void onAfk(AfkEvent event, EntityRef entityRef) {
|
||||
if (requireConnection() && player.getClientEntity().equals(entityRef)) {
|
||||
return;
|
||||
}
|
||||
if (event.isAfk()) {
|
||||
disableDiscord();
|
||||
} else {
|
||||
enableDiscord();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requireConnection() {
|
||||
NetworkMode networkMode = networkSystem.getMode();
|
||||
return networkMode != NetworkMode.CLIENT && networkMode != NetworkMode.DEDICATED_SERVER;
|
||||
}
|
||||
|
||||
private void enableDiscord() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
DiscordRPCSubSystem.setState("Idle", true);
|
||||
}
|
||||
|
||||
private void disableDiscord() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
DiscordRPCSubSystem.setState(getGame(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
DiscordRPCSubSystem.tryToDiscover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preBegin() {
|
||||
DiscordRPCSubSystem.setState(getGame(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBegin() {
|
||||
DiscordRPCSubSystem.setState(getGame(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
DiscordRPCSubSystem.setState("In Lobby");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# Subsystems
|
||||
|
||||
Subsystems provide and extend engine functionality that might not be needed all the time.
|
||||
|
||||
A subsystem can provide an API to be implemented by other subsystems (API subsystem). Such an API subsystem does not provide any functionality on its own.
|
||||
|
||||
Typical examples for subsystems are:
|
||||
* platform integration (e.g., Discord)
|
||||
* native libraries usage
|
||||
* network activity
|
||||
|
||||
> :warning: **Subsystems should not extend or provide gameplay features!** Use Modules instead.
|
||||
>
|
||||
> It is planned to allow combinations of modules with subsystems for new functionality in the future.
|
||||
|
||||
### Comparation between Module and Subsystem functionality:
|
||||
|
||||
| | Module | Subsystem
|
||||
--------------|---------------------------------------------------|----------
|
||||
Boot | at game start | at game launch
|
||||
Sandbox | Yes | No
|
||||
Installing | Yes, in-game download from server or repository | No, with facade or engine only
|
||||
Dependencies | only another Modules | any, except Modules
|
||||
Build Script | engine-driven | free-style
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// This magically allows subdirs in this subproject to themselves become sub-subprojects in a proper tree structure
|
||||
new File(rootDir, 'subsystems').eachDir { possibleSubprojectDir ->
|
||||
if (!possibleSubprojectDir.name.startsWith(".")) {
|
||||
def subprojectName = 'subsystems:' + possibleSubprojectDir.name
|
||||
logger.info("Including '$subprojectName' as a sub-project")
|
||||
include subprojectName
|
||||
def subprojectPath = ':' + subprojectName
|
||||
def subproject = project(subprojectPath)
|
||||
subproject.projectDir = possibleSubprojectDir
|
||||
} else {
|
||||
logger.info("Ignoring hidden folder '$possibleSubprojectDir'")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue