Specialized Fabric and Sponge version checkers (#2125)

Affects issues:
- Close #936
- Close #2093.
This commit is contained in:
Emilia Dreamer 2021-10-13 18:00:20 +03:00 committed by GitHub
parent bcfd52d76b
commit 4d151d45fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 375 additions and 19 deletions

View File

@ -42,16 +42,16 @@ import java.util.Optional;
@Singleton
public class VersionChecker implements SubSystem {
private final VersionNumber currentVersion;
private final Locale locale;
private final PlanConfig config;
private final PluginLogger logger;
private final RunnableFactory runnableFactory;
private final ErrorLogger errorLogger;
protected final VersionNumber currentVersion;
protected final Locale locale;
protected final PlanConfig config;
protected final PluginLogger logger;
protected final RunnableFactory runnableFactory;
protected final ErrorLogger errorLogger;
private static final String DOWNLOAD_ICON_HTML = "<i class=\"fa fa-fw fa-download\"></i> ";
private VersionInfo newVersionAvailable;
protected VersionInfo newVersionAvailable;
@Inject
public VersionChecker(
@ -74,9 +74,20 @@ public class VersionChecker implements SubSystem {
return newVersionAvailable != null;
}
private void checkForUpdates() {
protected Optional<List<VersionInfo>> loadVersionInfo() {
try {
List<VersionInfo> versions = VersionInfoLoader.load();
return Optional.of(VersionInfoLoader.load());
} catch (IOException e) {
errorLogger.warn(e, ErrorContext.builder()
.related(locale.getString(PluginLang.VERSION_FAIL_READ_VERSIONS))
.whatToDo("Allow Plan to check for updates from Github/versions.txt or disable update check.")
.build());
return Optional.empty();
}
}
private void checkForUpdates() {
loadVersionInfo().ifPresent(versions -> {
if (config.isFalse(PluginSettings.NOTIFY_ABOUT_DEV_RELEASES)) {
versions = Lists.filter(versions, VersionInfo::isRelease);
}
@ -84,9 +95,9 @@ public class VersionChecker implements SubSystem {
if (newestVersion.getVersion().isNewerThan(currentVersion)) {
newVersionAvailable = newestVersion;
String notification = locale.getString(
PluginLang.VERSION_AVAILABLE,
newestVersion.getVersion().asString(),
newestVersion.getChangeLogUrl()
PluginLang.VERSION_AVAILABLE,
newestVersion.getVersion().asString(),
newestVersion.getChangeLogUrl()
) + (newestVersion.isRelease() ? "" : locale.getString(PluginLang.VERSION_AVAILABLE_DEV));
logger.info("§a----------------------------------------");
logger.info("§a" + notification);
@ -94,14 +105,10 @@ public class VersionChecker implements SubSystem {
} else {
logger.info(locale.getString(PluginLang.VERSION_NEWEST));
}
} catch (IOException e) {
errorLogger.warn(e, ErrorContext.builder()
.related(locale.getString(PluginLang.VERSION_FAIL_READ_VERSIONS))
.whatToDo("Allow Plan to check for updates from Github/versions.txt or disable update check.")
.build());
}
});
}
@Override
public void enable() {
if (config.isFalse(PluginSettings.CHECK_FOR_UPDATES)) {

View File

@ -75,4 +75,14 @@ public class VersionInfo implements Comparable<VersionInfo> {
public int compareTo(VersionInfo o) {
return this.version.compareTo(o.version);
}
@Override
public String toString() {
return "VersionInfo{" +
"release=" + release +
", version=" + version +
", downloadUrl='" + downloadUrl + '\'' +
", changeLogUrl='" + changeLogUrl + '\'' +
'}';
}
}

View File

@ -23,7 +23,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VersionNumber implements Comparable<VersionNumber> {
private static final Pattern MATCH_NUMBERS = Pattern.compile("([0-9][0-9]*)");
private static final Pattern MATCH_NUMBERS = Pattern.compile("([0-9]+)");
private final String version;
private final List<Long> versionNumbers;

View File

@ -24,6 +24,7 @@ import com.djrapitops.plan.identification.ServerServerInfo;
import com.djrapitops.plan.settings.ConfigSystem;
import com.djrapitops.plan.settings.FabricConfigSystem;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.version.VersionChecker;
import dagger.Binds;
import dagger.Module;
import net.minecraft.server.world.ServerWorld;
@ -31,6 +32,7 @@ import net.playeranalytics.plan.FabricServerShutdownSave;
import net.playeranalytics.plan.gathering.FabricSensor;
import net.playeranalytics.plan.gathering.listeners.FabricListenerSystem;
import net.playeranalytics.plan.storage.database.FabricDBSystem;
import net.playeranalytics.plan.version.FabricVersionChecker;
/**
* Module for binding Fabric-specific classes as interface implementations.
@ -60,4 +62,7 @@ public interface FabricSuperClassBindingModule {
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<ServerWorld> sensor);
@Binds
VersionChecker bindVersionChecker(FabricVersionChecker versionChecker);
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package net.playeranalytics.plan.version;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.version.VersionChecker;
import com.djrapitops.plan.version.VersionInfo;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import net.playeranalytics.plugin.server.PluginLogger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Optional;
/**
* System for checking if new Version is available when the System initializes, altering the link for Fabric.
*/
@Singleton
public class FabricVersionChecker extends VersionChecker {
@Inject
public FabricVersionChecker(
@Named("currentVersion") String currentVersion,
Locale locale,
PlanConfig config,
PluginLogger logger,
RunnableFactory runnableFactory,
ErrorLogger errorLogger
) {
super(currentVersion, locale, config, logger, runnableFactory, errorLogger);
}
public Optional<VersionInfo> getNewVersionAvailable() {
if (newVersionAvailable == null) {
return Optional.empty();
} else {
return Optional.of(new VersionInfo(
newVersionAvailable.isRelease(),
newVersionAvailable.getVersion(),
newVersionAvailable.getDownloadUrl().replace("Plan-", "PlanFabric-"),
newVersionAvailable.getChangeLogUrl()
));
}
}
}

View File

@ -30,6 +30,8 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.SpongeDBSystem;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.storage.file.SpongePlanFiles;
import com.djrapitops.plan.version.SpongeVersionChecker;
import com.djrapitops.plan.version.VersionChecker;
import dagger.Binds;
import dagger.Module;
import org.spongepowered.api.world.World;
@ -65,4 +67,7 @@ public interface SpongeSuperClassBindingModule {
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<World> sensor);
@Binds
VersionChecker bindVersionChecker(SpongeVersionChecker versionChecker);
}

View File

@ -0,0 +1,64 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.version;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.version.ore.OreVersionInfoLoader;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import net.playeranalytics.plugin.server.PluginLogger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
/**
* System for checking if new Version is available when the System initializes, using the Ore API.
*/
@Singleton
public class SpongeVersionChecker extends VersionChecker {
@Inject
public SpongeVersionChecker(
@Named("currentVersion") String currentVersion,
Locale locale,
PlanConfig config,
PluginLogger logger,
RunnableFactory runnableFactory,
ErrorLogger errorLogger
) {
super(currentVersion, locale, config, logger, runnableFactory, errorLogger);
}
@Override
protected Optional<List<VersionInfo>> loadVersionInfo() {
try {
return Optional.of(OreVersionInfoLoader.load());
} catch (IOException e) {
errorLogger.warn(e, ErrorContext.builder()
.related(locale.getString(PluginLang.VERSION_FAIL_READ_VERSIONS))
.whatToDo("Allow Plan to check for updates from Ore or disable update check.")
.build());
return Optional.empty();
}
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.version.ore;
public class OreTagDto {
private final String name;
private final String data;
public OreTagDto(String name, String data) {
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public String getData() {
return data;
}
@Override
public String toString() {
return "OreTagDto{" +
"name='" + name + '\'' +
", data='" + data + '\'' +
'}';
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.version.ore;
import java.util.List;
public class OreVersionDto {
private final String name;
private final List<OreTagDto> tags;
public OreVersionDto(String name, List<OreTagDto> tags) {
this.name = name;
this.tags = tags;
}
public String getName() {
return name;
}
public List<OreTagDto> getTags() {
return tags;
}
@Override
public String toString() {
return "OreVersionDto{" +
"name='" + name + '\'' +
", tags=" + tags +
'}';
}
}

View File

@ -0,0 +1,116 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.version.ore;
import com.djrapitops.plan.version.VersionInfo;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Utility for loading version information from Ore, Sponge's plugin repository
*/
public class OreVersionInfoLoader {
private static final String ORE_AUTHENTICATE_URL = "https://ore.spongepowered.org/api/v2/authenticate";
private static final String ORE_VERSIONS_URL = "https://ore.spongepowered.org/api/v2/projects/plan/versions";
private static final String ORE_DOWNLOAD_URL = "https://ore.spongepowered.org/AuroraLS3/Plan/versions/%s/download";
private static final String ORE_CHANGE_LOG_URL = "https://ore.spongepowered.org/AuroraLS3/Plan/versions/%s#change-log";
private OreVersionInfoLoader() {
/* Static method class */
}
/**
* Loads version information from Ore, using its Web API.
*
* @return List of VersionInfo, newest version first.
* @throws IOException If API can not be accessed.
*/
public static List<VersionInfo> load() throws IOException {
List<VersionInfo> versionInfo = new ArrayList<>();
String session = newOreSession();
List<OreVersionDto> versions = loadOreVersions(session);
versions.forEach(i -> {
boolean isRelease = i.getTags().stream().anyMatch(t -> t.getName().equals("Channel") && t.getData().equals("Release"));
String spacedVersion = i.getName().replace('-', ' ');
String download = String.format(ORE_DOWNLOAD_URL, i.getName());
String changeLog = String.format(ORE_CHANGE_LOG_URL, i.getName());
versionInfo.add(new VersionInfo(isRelease, spacedVersion, download, changeLog));
});
Collections.sort(versionInfo);
return versionInfo;
}
private static List<OreVersionDto> loadOreVersions(String session) throws IOException {
URL url = new URL(ORE_VERSIONS_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", String.format("OreApi session=\"%s\"", session));
connection.connect();
try (InputStream in = connection.getInputStream()) {
JsonArray versions = new JsonParser().parse(readInputFully(in)).getAsJsonObject().get("result").getAsJsonArray();
return new Gson().getAdapter(new TypeToken<List<OreVersionDto>>() {}).fromJsonTree(versions);
}
} finally {
connection.disconnect();
}
}
private static String newOreSession() throws IOException {
URL url = new URL(ORE_AUTHENTICATE_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.connect();
try (InputStream in = connection.getInputStream()) {
return new JsonParser().parse(readInputFully(in)).getAsJsonObject().get("session").getAsString();
}
} finally {
connection.disconnect();
}
}
// I want Java 9 already...
private static String readInputFully(InputStream in) throws IOException {
try (ByteArrayOutputStream buf = new ByteArrayOutputStream(512)) {
int b;
while ((b = in.read()) != -1) {
buf.write((byte) b);
}
return buf.toString();
}
}
}