diff --git a/anvil-api/build.gradle b/anvil-api/build.gradle index 9b65e02fa..715f46193 100644 --- a/anvil-api/build.gradle +++ b/anvil-api/build.gradle @@ -3,6 +3,7 @@ version = rootProject.apiVersion dependencies { api bson api configurate_core + api(gson) api(guice + ":" + guice_version) api hikari api jedis diff --git a/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/Keys.java b/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/Keys.java index e59bac48f..a4cec41db 100644 --- a/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/Keys.java +++ b/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/Keys.java @@ -22,6 +22,8 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Table; +import com.google.common.reflect.TypeToken; +import org.anvilpowered.anvil.api.Tristate; import org.checkerframework.checker.nullness.qual.Nullable; import java.time.ZoneId; @@ -193,6 +195,11 @@ public static Map> getAll(String nameSpace) { .name("CACHE_INVALIDATION_TIMOUT_SECONDS") .fallback(300) .build(); + public static final Key CHECK_VERSIONS = + Key.builder(TypeTokens.TRISTATE) + .name("CHECK_VERSIONS") + .fallback(Tristate.TRUE) + .build(); public static final Key USE_SHARED_ENVIRONMENT = Key.builder(TypeTokens.BOOLEAN) .name("USE_SHARED_ENVIRONMENT") @@ -302,6 +309,11 @@ public static Map> getAll(String nameSpace) { .sensitive() .build(); + public static final Key DUMP_PERMISSION = + Key.builder(TypeTokens.STRING) + .name("DUMP_PERMISSION") + .fallback("anvil.admin.dump") + .build(); public static final Key PLUGINS_PERMISSION = Key.builder(TypeTokens.STRING) .name("PLUGINS_PERMISSION") @@ -327,6 +339,7 @@ public static Map> getAll(String nameSpace) { .register(BASE_SCAN_PACKAGE) .register(CACHE_INVALIDATION_INTERVAL_SECONDS) .register(CACHE_INVALIDATION_TIMOUT_SECONDS) + .register(CHECK_VERSIONS) .register(USE_SHARED_ENVIRONMENT) .register(USE_SHARED_CREDENTIALS) .register(DATA_DIRECTORY) @@ -345,6 +358,7 @@ public static Map> getAll(String nameSpace) { .register(REDIS_USE_AUTH); startRegistration("anvil") + .register(DUMP_PERMISSION) .register(PLUGINS_PERMISSION) .register(REGEDIT_PERMISSION) .register(RELOAD_PERMISSION) diff --git a/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/TypeTokens.java b/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/TypeTokens.java index 0a92d4970..7e229c9a9 100644 --- a/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/TypeTokens.java +++ b/anvil-api/src/main/java/org/anvilpowered/anvil/api/registry/TypeTokens.java @@ -19,6 +19,7 @@ package org.anvilpowered.anvil.api.registry; import com.google.common.reflect.TypeToken; +import org.anvilpowered.anvil.api.Tristate; import java.time.ZoneId; @@ -32,5 +33,6 @@ private TypeTokens() { public static final TypeToken BOOLEAN = TypeToken.of(Boolean.class); public static final TypeToken INTEGER = TypeToken.of(Integer.class); public static final TypeToken STRING = TypeToken.of(String.class); + public static final TypeToken TRISTATE = TypeToken.of(Tristate.class); public static final TypeToken ZONE_ID = TypeToken.of(ZoneId.class); } diff --git a/anvil-api/src/main/java/org/anvilpowered/anvil/base/registry/BaseConfigurationService.java b/anvil-api/src/main/java/org/anvilpowered/anvil/base/registry/BaseConfigurationService.java index 15cf6dc19..9b22e5ffe 100644 --- a/anvil-api/src/main/java/org/anvilpowered/anvil/base/registry/BaseConfigurationService.java +++ b/anvil-api/src/main/java/org/anvilpowered/anvil/base/registry/BaseConfigurationService.java @@ -90,6 +90,8 @@ protected void setOptions(@Nullable ConfigurationOptions options) { protected void withCore() { setName(Keys.SERVER_NAME, "server.name"); setDescription(Keys.SERVER_NAME, "\nServer name"); + setName(Keys.CHECK_VERSIONS, "server.checkUpdates"); + setDescription(Keys.CHECK_VERSIONS, "\nWhether Anvil should check online for a new version."); } private void withDataStoreCore0() { diff --git a/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/Tristate.kt b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/Tristate.kt new file mode 100644 index 000000000..a053210c0 --- /dev/null +++ b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/Tristate.kt @@ -0,0 +1,31 @@ +package org.anvilpowered.anvil.api + + +/** + * Represents three possible states. + * + * [TRUE] A positive setting + * [FALSE] A negative setting + * [UNDEFINED] A non-existent setting + */ +enum class Tristate(state: Boolean?) { + + /** + * Represents a positive state + */ + TRUE(true), + + /** + * Represents a negative state + */ + FALSE(false), + + /** + * Represents a non-existent state + */ + UNDEFINED(null); + + val isTrue = state == true + val isFalse = state == false + val isUndefined = state == null +} diff --git a/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/plugin/PluginInfo.kt b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/plugin/PluginInfo.kt index 0bf49e49d..7880d88d4 100644 --- a/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/plugin/PluginInfo.kt +++ b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/plugin/PluginInfo.kt @@ -37,5 +37,7 @@ interface PluginInfo : Named { val organizationName: String + val sourceUrl: String + val buildDate: String } diff --git a/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/util/InfoDumpService.kt b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/util/InfoDumpService.kt new file mode 100644 index 000000000..57bad298f --- /dev/null +++ b/anvil-api/src/main/kotlin/org/anvilpowered/anvil/api/util/InfoDumpService.kt @@ -0,0 +1,39 @@ +/* + * Anvil - AnvilPowered + * Copyright (C) 2020-2021 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package org.anvilpowered.anvil.api.util + +import org.anvilpowered.anvil.api.Environment + +interface InfoDumpService { + + /** + * Publishes the registry data for all loaded environments to anvil servers + * + * @param source User to send the URL to + */ + suspend fun publishInfo(source: TCommandSource) + + /** + * Publishes the registry data for all specific environments to anvil servers + * + * @param source User to send the URL to + * @param environments Environments to query registry data from + */ + suspend fun publishInfo(source: TCommandSource, vararg environments: Environment) +} diff --git a/anvil-bungee/build.gradle b/anvil-bungee/build.gradle index a771bac64..327c03cb1 100644 --- a/anvil-bungee/build.gradle +++ b/anvil-bungee/build.gradle @@ -18,7 +18,19 @@ dependencies { implementation javasisst implementation(kotlin_reflect + ":" + kotlin_version) implementation(kotlin_stdlib + ":" + kotlin_version) + implementation(kotlin_stdlib7 + ":" + kotlin_version) implementation(kotlin_stdlib8 + ":" + kotlin_version) + implementation(kotlinx_coroutines) + implementation(kotlinx_coroutines_core) + implementation(kotlinx_coroutines_jvm) + implementation(kotlinx_serialization) + implementation(ktor_cio) + implementation(ktor_core) + implementation(ktor_http) + implementation(ktor_http_jvm) + implementation(ktor_network) + implementation(ktor_network_jvm) + implementation(ktor_utils) implementation(kyori_api) implementation(kyori_bungee_serializer) implementation(kyori_examination) @@ -26,8 +38,6 @@ dependencies { implementation(kyori_legacy_serializer) implementation(kyori_gson_serializer) implementation(kyori_plain_serializer) - implementation kotlinx_coroutines - implementation kotlinx_serialization implementation microutils_logging implementation mongo_java_driver implementation typesafe_config @@ -59,7 +69,7 @@ shadowJar { include dependency(kotlin_reflect) include dependency(kotlin_stdlib) include dependency(kotlin_stdlib8) - include dependency(kotlinx_coroutines) + include dependency(kotlinx_coroutines_core) include dependency(kotlinx_serialization) include dependency(kyori_api) include dependency(kyori_examination) diff --git a/anvil-bungee/src/main/kotlin/org/anvilpowered/anvil/bungee/command/BungeeSimpleCommandService.kt b/anvil-bungee/src/main/kotlin/org/anvilpowered/anvil/bungee/command/BungeeSimpleCommandService.kt index 4775a1b6c..b08c85c93 100644 --- a/anvil-bungee/src/main/kotlin/org/anvilpowered/anvil/bungee/command/BungeeSimpleCommandService.kt +++ b/anvil-bungee/src/main/kotlin/org/anvilpowered/anvil/bungee/command/BungeeSimpleCommandService.kt @@ -19,6 +19,7 @@ package org.anvilpowered.anvil.bungee.command import com.google.inject.Inject +import kotlinx.coroutines.runBlocking import net.md_5.bungee.api.CommandSender import net.md_5.bungee.api.ProxyServer import net.md_5.bungee.api.plugin.Command @@ -39,7 +40,9 @@ class BungeeSimpleCommandService : CommonSimpleCommandService() { ) : Command(primaryAlias, null, *otherAliases) { override fun execute(source: CommandSender, context: Array) { if (delegate.canExecute(source)) { - delegate.execute(source, context) + runBlocking { + delegate.execute(source, context) + } } else { source.sendNoPermission() } diff --git a/anvil-common/build.gradle b/anvil-common/build.gradle index 2d2900cf5..4cc12ca9a 100644 --- a/anvil-common/build.gradle +++ b/anvil-common/build.gradle @@ -11,6 +11,8 @@ dependencies { api("net.kyori:adventure-text-serializer-legacy:4.5.0") api("net.kyori:adventure-text-serializer-plain:4.5.0") api(configurate_hocon) + api(ktor_core) + api(ktor_cio) api(kotlin_reflect + ":" + kotlin_version) api(kotlin_stdlib + ":" + kotlin_version) api(kotlin_stdlib8 + ":" + kotlin_version) diff --git a/anvil-common/src/main/java/org/anvilpowered/anvil/api/EnvironmentBuilderImpl.java b/anvil-common/src/main/java/org/anvilpowered/anvil/api/EnvironmentBuilderImpl.java index c0c42ccf9..ff7d90fd6 100644 --- a/anvil-common/src/main/java/org/anvilpowered/anvil/api/EnvironmentBuilderImpl.java +++ b/anvil-common/src/main/java/org/anvilpowered/anvil/api/EnvironmentBuilderImpl.java @@ -33,6 +33,7 @@ import org.anvilpowered.anvil.api.registry.RegistryScope; import org.anvilpowered.anvil.common.PlatformImpl; import org.anvilpowered.anvil.common.module.PlatformModule; +import org.anvilpowered.anvil.common.util.VersionChecker; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; @@ -138,6 +139,7 @@ protected void configure() { .accept(injector.getInstance(entry.getKey())); } Registry registry = injector.getInstance(Registry.class); + registry.whenLoaded(injector.getInstance(VersionChecker.class)::checkVersion).register(); for (Consumer listener : loadedListeners.get(environment.getName())) { registry.whenLoaded(() -> listener.accept(environment)).register(); diff --git a/anvil-common/src/main/java/org/anvilpowered/anvil/common/plugin/AnvilPluginInfo.java b/anvil-common/src/main/java/org/anvilpowered/anvil/common/plugin/AnvilPluginInfo.java index 37ffaf4fe..c95e4972f 100644 --- a/anvil-common/src/main/java/org/anvilpowered/anvil/common/plugin/AnvilPluginInfo.java +++ b/anvil-common/src/main/java/org/anvilpowered/anvil/common/plugin/AnvilPluginInfo.java @@ -29,6 +29,7 @@ public class AnvilPluginInfo implements PluginInfo { public static final String description = "A cross-platform Minecraft plugin framework"; public static final String url = "https://github.com/AnvilPowered/Anvil"; public static final String organizationName = "AnvilPowered"; + public static final String sourceUrl = url; public static final String[] authors = {organizationName}; public static final String buildDate = "$buildDate"; public Component pluginPrefix = Component.text() @@ -71,8 +72,12 @@ public String[] getAuthors() { public String getOrganizationName() { return organizationName; } - @Override + public String getSourceUrl() { + return sourceUrl; + } + + @Override public String getBuildDate() { return buildDate; } diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilCommandNode.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilCommandNode.kt index 0d98f0f8e..617659640 100644 --- a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilCommandNode.kt +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilCommandNode.kt @@ -27,6 +27,7 @@ import org.anvilpowered.anvil.common.command.regedit.CommonRegistryEditCommandNo class CommonAnvilCommandNode @Inject constructor( private val commandService: SimpleCommandService, + private val dumpCommand: CommonAnvilDumpCommand, private val pluginsCommand: CommonAnvilPluginsCommand, private val reloadCommand: CommonAnvilReloadCommand, private val callbackCommand: CommonCallbackCommand, @@ -44,6 +45,7 @@ class CommonAnvilCommandNode @Inject constructor } val CALLBACK_ALIAS = listOf("callback") + val DUMP_ALIAS = listOf("dump") val HELP_ALIAS = listOf("help") val PLUGINS_ALIAS = listOf("plugins") val RELOAD_ALIAS = listOf("reload") @@ -71,6 +73,7 @@ class CommonAnvilCommandNode @Inject constructor private fun loadCommands() { val subCommands = listOf( commandService.mapTerminal(HELP_ALIAS, commandService.generateHelp { anvilMapping.subCommands }), + commandService.mapTerminal(DUMP_ALIAS, dumpCommand), commandService.mapTerminal(PLUGINS_ALIAS, pluginsCommand), regeditNode.regeditMapping, commandService.mapTerminal(RELOAD_ALIAS, reloadCommand), diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilDumpCommand.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilDumpCommand.kt new file mode 100644 index 000000000..75b2c8057 --- /dev/null +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonAnvilDumpCommand.kt @@ -0,0 +1,91 @@ +/* + * Anvil - AnvilPowered + * Copyright (C) 2020 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +package org.anvilpowered.anvil.common.command + +import com.google.inject.Inject +import java.util.Optional +import java.util.stream.Collectors +import kotlinx.coroutines.runBlocking +import org.anvilpowered.anvil.api.Anvil +import org.anvilpowered.anvil.api.Environment +import org.anvilpowered.anvil.api.command.SimpleCommand +import org.anvilpowered.anvil.api.misc.Named +import org.anvilpowered.anvil.api.util.InfoDumpService +import org.anvilpowered.anvil.api.util.TextService + +class CommonAnvilDumpCommand : SimpleCommand { + + @Inject + private lateinit var dumpService: InfoDumpService + + @Inject + private lateinit var textService: TextService + + override fun execute(source: TCommandSource, context: Array) { + if (context.isEmpty()) { + textService.builder() + .appendPrefix() + .red().append("Plugin is required if '--all' is not set") + .sendTo(source) + return + } + if ("-a" == context[0] || "--all" == context[0]) { + runBlocking { + dumpService.publishInfo(source) + } + return + } else { + runBlocking { + dumpDirect(source, context) + } + } + } + + override fun suggest(source: TCommandSource, context: Array): List { + val suggestions = Anvil.getEnvironmentManager() + .environments.values.stream() + .map(Named::name) + .sorted().collect(Collectors.toList()) + suggestions.add("--all") + return suggestions + } + + private suspend fun dumpDirect(source: TCommandSource, plugins: Array): Boolean { + val optionalEnvironments: MutableList> = mutableListOf() + for (environment in Anvil.getEnvironmentManager().environments.values) { + if (plugins.contains(environment.name)) { + optionalEnvironments.add(Optional.of(environment)) + } + } + if (optionalEnvironments.isEmpty()) { + textService.builder() + .appendPrefix() + .red().append("Could not find plugin(s) ") + .gold().append(plugins.joinToString(separator = " ")) + .sendTo(source) + return false + } + val environments: MutableList = mutableListOf() + for (env in optionalEnvironments) { + environments.add(env.get()) + } + dumpService.publishInfo(source, *environments.toTypedArray()) + return true + } +} diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonSimpleCommandService.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonSimpleCommandService.kt index 19d8a09d1..cbc193ea3 100644 --- a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonSimpleCommandService.kt +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/command/CommonSimpleCommandService.kt @@ -17,8 +17,10 @@ */ package org.anvilpowered.anvil.common.command +import com.google.common.base.MoreObjects import com.google.inject.Inject import net.kyori.adventure.text.Component +import org.anvilpowered.anvil.api.Anvil import org.anvilpowered.anvil.api.Environment import org.anvilpowered.anvil.api.command.CommandMapping import org.anvilpowered.anvil.api.command.SimpleCommand diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/module/CommonModule.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/module/CommonModule.kt index e77cdd2b6..b5a3d57a6 100644 --- a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/module/CommonModule.kt +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/module/CommonModule.kt @@ -33,12 +33,14 @@ import org.anvilpowered.anvil.api.misc.withMongoDB import org.anvilpowered.anvil.api.misc.withXodus import org.anvilpowered.anvil.api.plugin.PluginInfo import org.anvilpowered.anvil.api.registry.Registry +import org.anvilpowered.anvil.api.util.InfoDumpService import org.anvilpowered.anvil.common.command.CommonCallbackCommand import org.anvilpowered.anvil.common.coremember.CommonCoreMemberManager import org.anvilpowered.anvil.common.coremember.CommonMongoCoreMemberRepository import org.anvilpowered.anvil.common.coremember.CommonXodusCoreMemberRepository import org.anvilpowered.anvil.common.plugin.AnvilPluginInfo import org.anvilpowered.anvil.common.registry.CommonConfigurationService +import org.anvilpowered.anvil.common.util.CommonInfoDumpService import org.bson.types.ObjectId import java.nio.file.Paths diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/plugin/FallbackPluginInfo.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/plugin/FallbackPluginInfo.kt index 00348462e..36c2514ce 100644 --- a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/plugin/FallbackPluginInfo.kt +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/plugin/FallbackPluginInfo.kt @@ -31,6 +31,7 @@ class FallbackPluginInfo : PluginInfo { const val url = "URL" val authors = arrayOf("author") const val organizationName = "organizationName" + const val sourceUrl = "sourceUrl" const val buildDate = "last night" } @@ -51,6 +52,7 @@ class FallbackPluginInfo : PluginInfo { override val url: String = Companion.url override val authors: Array = Companion.authors override val organizationName: String = Companion.organizationName + override val sourceUrl: String = Companion.sourceUrl override val buildDate: String = Companion.buildDate override val prefix: Component = pluginPrefix } diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/registry/CommonConfigurationService.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/registry/CommonConfigurationService.kt index 92f640441..90fdae14f 100644 --- a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/registry/CommonConfigurationService.kt +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/registry/CommonConfigurationService.kt @@ -23,12 +23,15 @@ import ninja.leaping.configurate.ConfigurationOptions import ninja.leaping.configurate.commented.CommentedConfigurationNode import ninja.leaping.configurate.loader.ConfigurationLoader import ninja.leaping.configurate.objectmapping.serialize.TypeSerializerCollection +import org.anvilpowered.anvil.api.Platform +import org.anvilpowered.anvil.api.Tristate import org.anvilpowered.anvil.api.registry.Keys import org.anvilpowered.anvil.base.registry.BaseConfigurationService @Singleton -open class CommonConfigurationService @Inject constructor( +class CommonConfigurationService @Inject constructor( configLoader: ConfigurationLoader, + platform: Platform ) : BaseConfigurationService(configLoader) { init { withDataStoreCore() @@ -44,6 +47,9 @@ The server's timezone id. Use "auto" for the local system time, otherwise please see https://nodatime.org/TimeZones (note that your system's available timezones may differ). This option is useful if your server machine and community are based in different timezones. """) + if (platform.name.contains("sponge")) { + setDefault(Keys.CHECK_VERSIONS, Tristate.UNDEFINED) + } val serializers = TypeSerializerCollection.defaults().newChild() serializers.register(Keys.TIME_ZONE.type, CommonZoneIdSerializer()) val options = ConfigurationOptions.defaults() diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/CommonInfoDumpService.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/CommonInfoDumpService.kt new file mode 100644 index 000000000..05736f768 --- /dev/null +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/CommonInfoDumpService.kt @@ -0,0 +1,160 @@ +/* + * Anvil - AnvilPowered + * Copyright (C) 2020-2021 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package org.anvilpowered.anvil.common.util + +import com.google.gson.GsonBuilder +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.inject.Inject +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.post +import io.ktor.client.request.url +import io.ktor.client.statement.HttpResponse +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.utils.io.readUTF8Line +import kotlinx.coroutines.GlobalScope +import org.anvilpowered.anvil.api.Anvil +import org.anvilpowered.anvil.api.Environment +import org.anvilpowered.anvil.api.plugin.PluginInfo +import org.anvilpowered.anvil.api.registry.Key +import org.anvilpowered.anvil.api.registry.Keys +import org.anvilpowered.anvil.api.registry.Registry +import org.anvilpowered.anvil.api.util.InfoDumpService +import org.anvilpowered.anvil.api.util.TextService +import org.slf4j.Logger + +class CommonInfoDumpService : InfoDumpService { + + @Inject + private lateinit var logger: Logger + + @Inject + private lateinit var textService: TextService + + private val gson = GsonBuilder() + .setPrettyPrinting() + .excludeFieldsWithoutExposeAnnotation() + .setLenient() + .create() + + private fun collectInformation(environment: Environment): JsonElement { + val result = JsonObject() + val pluginInfo = JsonObject() + val info = environment.injector.getInstance(PluginInfo::class.java) + pluginInfo.addProperty("version", info.version) + pluginInfo.addProperty("description", info.description) + pluginInfo.addProperty("url", info.url) + pluginInfo.addProperty("authors", info.authors.joinToString(", ")) + pluginInfo.addProperty("buildDate", info.buildDate) + result.add("pluginInfo", pluginInfo) + val keys = JsonObject() + for (it in Keys.getAll(environment.name)) { + if (it.value.isSensitive) { + keys.addProperty(it.key, "***") + } else { + keys.addProperty(it.key, toString(it.value, environment.registry)) + } + } + result.add("keys", keys) + return result + } + + private fun collectSystemInfo(): JsonElement { + val sysInfo = JsonObject() + sysInfo.addProperty("os", System.getProperty("os.name")) + sysInfo.addProperty("osVersion", System.getProperty("os.version")) + sysInfo.addProperty("architecture", System.getProperty("os.arch")) + sysInfo.addProperty("javaVersion", System.getProperty("java.version")) + sysInfo.addProperty("javaVendor", System.getProperty("java.vendor")) + sysInfo.addProperty("platform", Anvil.getPlatform().name) + return sysInfo + } + + override suspend fun publishInfo(source: TCommandSource) { + val data = JsonObject() + val plugins = JsonObject() + data.add("system", collectSystemInfo()) + plugins.add("anvil", collectInformation(Anvil.getEnvironment())) + for (env in Anvil.getEnvironmentManager().environments.values) { + if (env.name == "anvil") { + continue + } + plugins.add(env.name, collectInformation(env)) + } + data.add("plugins", plugins) + publish(source, data.asJsonObject) + } + + override suspend fun publishInfo(source: TCommandSource, vararg environments: Environment) { + val data = JsonObject() + val plugins = JsonObject() + data.add("system", collectSystemInfo()) + // Always add the Anvil environment, regardless of whether it is in the provided environment vararg + plugins.add("anvil", collectInformation(Anvil.getEnvironment())) + for (env in environments) { + if (env.name == "anvil") { + continue + } + plugins.add(env.name, collectInformation(env)) + } + data.add("plugins", plugins) + publish(source, data.asJsonObject) + } + + private suspend fun publish(source: TCommandSource, data: JsonObject) { + val client = HttpClient(CIO) + val readable = GsonBuilder() + .setPrettyPrinting() + .create().toJson(data) + val requestBuilder = HttpRequestBuilder() + requestBuilder.url("http://dump.anvilpowered.org/dump") + requestBuilder.contentType(ContentType.Text.Plain) + requestBuilder.body = readable.toByteArray(Charsets.UTF_8) + GlobalScope.runCatching { + val resp = client.post(requestBuilder) + if (resp.status.value != 200) { + logger.error( + """ + An error occurred while attempting to post your dump. + The server may be down at this time or there is an issue with your internet connection. + If you believe this may be a bug, please contact the Anvil discord server. + """.trimIndent()) + return@runCatching + } + val key = parse(resp.content.readUTF8Line()) + check(key.has("key")) + val url = "http://dump.anvilpowered.org/${key.get("key").asString}.json" + textService.builder() + .appendPrefix() + .green().append("If a developer has requested you run this command, please provide them with the following link\n") + .gold().append(url).onClickOpenUrl(url) + .sendTo(source) + }.getOrElse { it.printStackTrace() } + } + + private fun parse(string: String?): JsonObject { + check(string != null) { "An error occurred while posting the data" } + return gson.fromJson(string, JsonObject::class.java) + } + + fun toString(key: Key, registry: Registry): String = key.toString(registry.getOrDefault(key)) +} diff --git a/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/VersionChecker.kt b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/VersionChecker.kt new file mode 100644 index 000000000..05dfe3947 --- /dev/null +++ b/anvil-common/src/main/kotlin/org/anvilpowered/anvil/common/util/VersionChecker.kt @@ -0,0 +1,122 @@ +package org.anvilpowered.anvil.common.util + +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.inject.Inject +import com.google.inject.Singleton +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.request +import io.ktor.client.request.url +import io.ktor.client.statement.HttpResponse +import io.ktor.utils.io.readUTF8Line +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.anvilpowered.anvil.api.Tristate +import org.anvilpowered.anvil.api.plugin.PluginInfo +import org.anvilpowered.anvil.api.registry.Keys +import org.anvilpowered.anvil.api.registry.Registry +import org.anvilpowered.anvil.api.util.TextService +import java.net.URL +import java.util.Scanner + +@Singleton +class VersionChecker @Inject constructor( + private val pluginInfo: PluginInfo, + private val registry: Registry, + private val textService: TextService<*> +) { + + private var url = "" + private var currentVersion = "" + + private val client = HttpClient(CIO) + private val request = HttpRequestBuilder() + + fun checkVersion() { + + if (registry.getOrDefault(Keys.CHECK_VERSIONS) == Tristate.UNDEFINED + || registry.getOrDefault(Keys.CHECK_VERSIONS) == Tristate.FALSE + ) { + textService.builder() + .appendPrefix() + .red().append("Version checking disabled! You will have to manually check for updates. ") + .append("If you would like to enable auto-checking, enable 'checkVersions' in the config!") + .sendToConsole() + return + } + + val specified = pluginInfo.sourceUrl + currentVersion = pluginInfo.version + if (currentVersion.isEmpty()) { + return + } + if (currentVersion.contains("-SNAPSHOT")) { + textService.builder() + .appendPrefix() + .red().append("You are currently running a development build! AnvilPowered does not recommend running development builds in production environments!") + .sendToConsole() + return + } + when { + specified.contains("github") -> { + val split = specified.substring(19, specified.length).split(Regex("/")) + url = "https://api.github.com/repos/${split[0]}/${split[1]}/releases/latest" + checkGithub() + } + specified.contains("spigot") -> { + val resource = specified.substring(specified.indexOfLast { it == '.' } + 1, specified.length - 1) + url = "https://api.spigotmc.org/legacy/update.php?resource=$resource" + checkSpigot() + } + } + } + + private fun checkGithub() { + request.url(url) + GlobalScope.launch { + val response = client.request(request) + val gson = Gson().fromJson(response.content.readUTF8Line(), JsonObject::class.java) + check(gson.has("tag_name")) { "An error occurred while finding the latest release from GitHub!" } + var latest = gson.get("tag_name").asString + latest = latest.replace(Regex("[a-zA-Z]"), "") + compareVersions(latest) + } + } + + private fun checkSpigot() { + try { + val scanner = Scanner(URL(url).openStream()) + if (scanner.hasNext()) { + compareVersions(scanner.next()) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun compareVersions(latest: String) { + val split = latest.split(Regex("\\.")) + val lMaj = split[0].toInt() + val lMin = split[1].toInt() + val lBug = split[2].toInt() + + val splitCurrent = currentVersion.split(Regex("\\.")) + val cMaj = splitCurrent[0].toInt() + val cMin = splitCurrent[1].toInt() + val cBug = splitCurrent[2].toInt() + + if (lMaj > cMaj + || lMin > cMin + || lBug > cBug + ) { + textService.builder() + .appendPrefix() + .red().append("You are running an outdated version of ${pluginInfo.name}, please update to ") + .gold().append("[$latest]") + .red().append("! (installed: $currentVersion)") + .sendToConsole() + } + } +} diff --git a/anvil-nukkit/build.gradle b/anvil-nukkit/build.gradle index 1a21d963b..4d30e038b 100644 --- a/anvil-nukkit/build.gradle +++ b/anvil-nukkit/build.gradle @@ -21,9 +21,19 @@ dependencies { implementation javasisst implementation(kotlin_reflect + ":" + kotlin_version) implementation(kotlin_stdlib + ":" + kotlin_version) + implementation(kotlin_stdlib7 + ":" + kotlin_version) implementation(kotlin_stdlib8 + ":" + kotlin_version) - implementation kotlinx_coroutines - implementation kotlinx_serialization + implementation(kotlinx_coroutines) + implementation(kotlinx_coroutines_core) + implementation(kotlinx_coroutines_jvm) + implementation(kotlinx_serialization) + implementation(ktor_cio) + implementation(ktor_core) + implementation(ktor_http) + implementation(ktor_http_jvm) + implementation(ktor_network) + implementation(ktor_network_jvm) + implementation(ktor_utils) implementation(kyori_examination) implementation(kyori_key) implementation(kyori_legacy_serializer) @@ -59,9 +69,20 @@ shadowJar { include dependency(jedis) include dependency(kotlin_reflect) include dependency(kotlin_stdlib) + include dependency(kotlin_stdlib7) include dependency(kotlin_stdlib8) include dependency(kotlinx_coroutines) + include dependency(kotlinx_coroutines_core) + include dependency(kotlinx_coroutines_jvm) include dependency(kotlinx_serialization) + include dependency(ktor_core) + include dependency(ktor_cio) + include dependency(ktor_http) + include dependency(ktor_http_jvm) + include dependency(ktor_io) + include dependency(ktor_network) + include dependency(ktor_network_jvm) + include dependency(ktor_utils) include dependency(kyori_api) include dependency(kyori_examination) include dependency(kyori_key) diff --git a/anvil-spigot/build.gradle b/anvil-spigot/build.gradle index 70caec382..d496907af 100644 --- a/anvil-spigot/build.gradle +++ b/anvil-spigot/build.gradle @@ -22,8 +22,19 @@ dependencies { implementation javasisst implementation(kotlin_reflect + ":" + kotlin_version) implementation(kotlin_stdlib + ":" + kotlin_version) + implementation(kotlin_stdlib7 + ":" + kotlin_version) implementation(kotlin_stdlib8 + ":" + kotlin_version) - implementation kotlinx_coroutines + implementation(kotlinx_coroutines) + implementation(kotlinx_coroutines_core) + implementation(kotlinx_coroutines_jvm) + implementation(kotlinx_serialization) + implementation(ktor_cio) + implementation(ktor_core) + implementation(ktor_http) + implementation(ktor_http_jvm) + implementation(ktor_network) + implementation(ktor_network_jvm) + implementation(ktor_utils) implementation(kyori_api) implementation(kyori_bukkit_serializer) implementation(kyori_examination) @@ -32,7 +43,6 @@ dependencies { implementation(kyori_gson_serializer) implementation(kyori_gson_legacy) implementation(kyori_platform) - implementation kotlinx_serialization implementation microutils_logging implementation mongo_java_driver implementation spigot @@ -65,9 +75,20 @@ shadowJar { include dependency(jedis) include dependency(kotlin_reflect) include dependency(kotlin_stdlib) + include dependency(kotlin_stdlib7) include dependency(kotlin_stdlib8) include dependency(kotlinx_coroutines) + include dependency(kotlinx_coroutines_core) + include dependency(kotlinx_coroutines_jvm) include dependency(kotlinx_serialization) + include dependency(ktor_core) + include dependency(ktor_cio) + include dependency(ktor_http) + include dependency(ktor_http_jvm) + include dependency(ktor_io) + include dependency(ktor_network) + include dependency(ktor_network_jvm) + include dependency(ktor_utils) include dependency(kyori_api) include dependency(kyori_examination) include dependency(kyori_key) diff --git a/anvil-sponge/build.gradle b/anvil-sponge/build.gradle index 5a136b397..06bbb8f46 100644 --- a/anvil-sponge/build.gradle +++ b/anvil-sponge/build.gradle @@ -22,9 +22,19 @@ dependencies { implementation javasisst implementation(kotlin_reflect + ":" + kotlin_version) implementation(kotlin_stdlib + ":" + kotlin_version) + implementation(kotlin_stdlib7 + ":" + kotlin_version) implementation(kotlin_stdlib8 + ":" + kotlin_version) - implementation kotlinx_coroutines - implementation kotlinx_serialization + implementation(kotlinx_coroutines) + implementation(kotlinx_coroutines_core) + implementation(kotlinx_coroutines_jvm) + implementation(kotlinx_serialization) + implementation(ktor_cio) + implementation(ktor_core) + implementation(ktor_http) + implementation(ktor_http_jvm) + implementation(ktor_network) + implementation(ktor_network_jvm) + implementation(ktor_utils) implementation microutils_logging implementation mongo_java_driver } @@ -52,9 +62,20 @@ shadowJar { include dependency(jedis) include dependency(kotlin_reflect) include dependency(kotlin_stdlib) + include dependency(kotlin_stdlib7) include dependency(kotlin_stdlib8) include dependency(kotlinx_coroutines) + include dependency(kotlinx_coroutines_core) + include dependency(kotlinx_coroutines_jvm) include dependency(kotlinx_serialization) + include dependency(ktor_core) + include dependency(ktor_cio) + include dependency(ktor_http) + include dependency(ktor_http_jvm) + include dependency(ktor_io) + include dependency(ktor_network) + include dependency(ktor_network_jvm) + include dependency(ktor_utils) include dependency(kyori_api) include dependency(kyori_examination) include dependency(kyori_key) diff --git a/anvil-velocity/build.gradle b/anvil-velocity/build.gradle index 9439a75d7..669369ee4 100644 --- a/anvil-velocity/build.gradle +++ b/anvil-velocity/build.gradle @@ -22,15 +22,31 @@ dependencies { implementation javasisst implementation(kotlin_reflect + ":" + kotlin_version) implementation(kotlin_stdlib + ":" + kotlin_version) + implementation(kotlin_stdlib7 + ":" + kotlin_version) implementation(kotlin_stdlib8 + ":" + kotlin_version) - implementation kotlinx_coroutines - implementation kotlinx_serialization + implementation(kotlinx_coroutines) + implementation(kotlinx_coroutines_core) + implementation(kotlinx_coroutines_jvm) + implementation(kotlinx_serialization) + implementation(ktor_cio) + implementation(ktor_core) + implementation(ktor_http) + implementation(ktor_http_jvm) + implementation(ktor_network) + implementation(ktor_network_jvm) + implementation(ktor_utils) implementation microutils_logging implementation mongo_java_driver implementation velocity kapt velocity } +tasks.register("copyJars", Copy) { + dependsOn(tasks.shadowJar) + from(tasks.shadowJar) + into("C:\\Users\\Allen\\Desktop\\Velocity\\plugins") +} + shadowJar { String jarName = "Anvil-Velocity-${project.version}.jar" println "Building: " + jarName @@ -51,9 +67,20 @@ shadowJar { include dependency(jedis) include dependency(kotlin_reflect) include dependency(kotlin_stdlib) + include dependency(kotlin_stdlib7) include dependency(kotlin_stdlib8) include dependency(kotlinx_coroutines) + include dependency(kotlinx_coroutines_core) + include dependency(kotlinx_coroutines_jvm) include dependency(kotlinx_serialization) + include dependency(ktor_core) + include dependency(ktor_cio) + include dependency(ktor_http) + include dependency(ktor_http_jvm) + include dependency(ktor_io) + include dependency(ktor_network) + include dependency(ktor_network_jvm) + include dependency(ktor_utils) include dependency(microutils_logging) include dependency(mongo_java_driver) include dependency(mongodb_driver_sync) diff --git a/anvil-velocity/src/main/kotlin/org/anvilpowered/anvil/velocity/module/ApiVelocityModule.kt b/anvil-velocity/src/main/kotlin/org/anvilpowered/anvil/velocity/module/ApiVelocityModule.kt index a0e3e0963..5c3e68345 100644 --- a/anvil-velocity/src/main/kotlin/org/anvilpowered/anvil/velocity/module/ApiVelocityModule.kt +++ b/anvil-velocity/src/main/kotlin/org/anvilpowered/anvil/velocity/module/ApiVelocityModule.kt @@ -25,6 +25,7 @@ import org.anvilpowered.anvil.api.command.SimpleCommandService import org.anvilpowered.anvil.api.misc.bind import org.anvilpowered.anvil.api.misc.to import org.anvilpowered.anvil.api.server.LocationService +import org.anvilpowered.anvil.api.util.InfoDumpService import org.anvilpowered.anvil.api.util.KickService import org.anvilpowered.anvil.api.util.PermissionService import org.anvilpowered.anvil.api.util.TextService @@ -32,6 +33,7 @@ import org.anvilpowered.anvil.api.util.UserService import org.anvilpowered.anvil.common.PlatformImpl import org.anvilpowered.anvil.common.command.CommonCallbackCommand import org.anvilpowered.anvil.common.module.PlatformModule +import org.anvilpowered.anvil.common.util.CommonInfoDumpService import org.anvilpowered.anvil.common.util.CommonTextService import org.anvilpowered.anvil.common.util.SendTextService import org.anvilpowered.anvil.velocity.command.VelocityCommandExecuteService @@ -55,6 +57,7 @@ class ApiVelocityModule : PlatformModule( bind>() bind().to() bind>().to() + bind>().to>() bind().to() bind().to() bind().to() diff --git a/gradle.properties b/gradle.properties index 70fd21b0a..42977da28 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,7 @@ bson=org.mongodb:bson:3.12.0 bungee=net.md-5:bungeecord-api:1.15-SNAPSHOT configurate_core=org.spongepowered:configurate-core:3.7.2 configurate_hocon=org.spongepowered:configurate-hocon:3.7.2 +gson=com.google.code.gson:gson:2.8.6 guava=com.google.guava:guava:28.1-jre guice=com.google.inject:guice guice_version=4.1.0 @@ -15,10 +16,22 @@ jedis=redis.clients:jedis:3.2.0 jetbrains_annotations=org.jetbrains:annotations:20.1.0 kotlin_reflect=org.jetbrains.kotlin:kotlin-reflect kotlin_stdlib=org.jetbrains.kotlin:kotlin-stdlib +kotlin_stdlib7=org.jetbrains.kotlin:kotlin-stdlib-jdk7 kotlin_stdlib8=org.jetbrains.kotlin:kotlin-stdlib-jdk8 kotlin_version=1.4.30 -kotlinx_coroutines=org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.2 +kotlinx_coroutines=org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3 +kotlinx_coroutines_core=org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2-native-mt +kotlinx_coroutines_jvm=org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3 kotlinx_serialization=org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0 +ktor_version=1.5.2 +ktor_cio=io.ktor:ktor-client-cio-jvm:1.5.2 +ktor_core=io.ktor:ktor-client-core-jvm:1.5.2 +ktor_http=io.ktor:ktor-http-cio-jvm:1.5.2 +ktor_http_jvm=io.ktor:ktor-http-jvm:1.5.2 +ktor_network=io.ktor:ktor-network-tls-jvm:1.5.2 +ktor_network_jvm=io.ktor:ktor-network-jvm:1.5.2 +ktor_utils=io.ktor:ktor-utils-jvm:1.5.2 +ktor_io=io.ktor:ktor-io-jvm:1.5.2 kyori_api=net.kyori:adventure-api:4.5.1 kyori_examination=net.kyori:examination-api:1.0.0 kyori_key=net.kyori:adventure-key:4.5.1