Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #34 from Andre601/feature/update-checker
Browse files Browse the repository at this point in the history
Add Update Checker
  • Loading branch information
Andre601 authored Dec 6, 2022
2 parents 9db9f2b + ad3ba0c commit 6f06c47
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 9 deletions.
4 changes: 4 additions & 0 deletions bungeecord/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.caffeine</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.okhttp3</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import ch.andre601.advancedserverlist.bungeecord.events.PingEvent;
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.core.ProxyCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.ProxyCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.placeholders.Placeholders;
Expand Down Expand Up @@ -122,6 +122,11 @@ public String getPlatformVersion(){
return getProxy().getVersion();
}

@Override
public String getLoader(){
return "bungeecord";
}

@Override
public List<ServerPing.PlayerInfo> createPlayers(List<String> lines, Placeholders... placeholders){
List<ServerPing.PlayerInfo> players = new ArrayList<>(lines.size());
Expand Down
10 changes: 10 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@
<version>3.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package ch.andre601.advancedserverlist.core;

import ch.andre601.advancedserverlist.core.check.UpdateChecker;
import ch.andre601.advancedserverlist.core.commands.CommandHandler;
import ch.andre601.advancedserverlist.core.file.FileHandler;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
Expand All @@ -33,7 +34,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
import java.util.Properties;

public class AdvancedServerList{

Expand All @@ -42,6 +43,8 @@ public class AdvancedServerList{
private final CommandHandler commandHandler;
private final PlayerHandler playerHandler;

private UpdateChecker updateChecker;

private String version;

public AdvancedServerList(PluginCore<?> plugin){
Expand Down Expand Up @@ -76,6 +79,12 @@ public String getVersion(){
public void disable(){
getPlugin().getPluginLogger().info("Saving cache.data file...");
getPlayerHandler().save();

if(updateChecker != null){
getPlugin().getPluginLogger().info("Disabling Update Checker...");
updateChecker.disable();
}

getPlugin().getPluginLogger().info("AdvancedServerList disabled!");
}

Expand All @@ -88,7 +97,6 @@ private void load(){
resolveVersion();

getPlugin().getPluginLogger().info("Starting AdvancedServerList v%s...", version);

getPlugin().getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());

if(getFileHandler().loadConfig()){
Expand Down Expand Up @@ -125,6 +133,9 @@ private void load(){
getPlugin().getPluginLogger().info("Metrics loaded!");

getPlugin().getPluginLogger().info("AdvancedServerList is ready!");

if(getFileHandler().getBoolean("check_updates"))
this.updateChecker = new UpdateChecker(this);
}

private void printBanner(){
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package ch.andre601.advancedserverlist.core.check;

import org.jetbrains.annotations.NotNull;

import java.util.concurrent.ThreadFactory;

public class UpdateCheckThread implements ThreadFactory{
@Override
public Thread newThread(@NotNull Runnable r){
return new Thread(r, "AdvancedServerList Update-Thread");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package ch.andre601.advancedserverlist.core.check;

import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import io.leangen.geantyref.TypeToken;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class UpdateChecker{

private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=[\"%s\"]";
private final OkHttpClient client = new OkHttpClient();

private final AdvancedServerList core;
private final PluginLogger logger;
private final String loader;

private final Type listType = new TypeToken<ArrayList<ModrinthVersion>>(){}.getType();
private final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.setLenient()
.create();

private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new UpdateCheckThread());

public UpdateChecker(AdvancedServerList core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
this.loader = core.getPlugin().getLoader();

startUpdateChecker();
}

public void startUpdateChecker(){
executor.scheduleAtFixedRate(() -> {
logger.info("Checking for a new update...");
checkUpdate().whenComplete((version, throwable) -> {
if(version == null || throwable != null){
logger.warn("Failed to look for any updates. See previous messages for reasons.");
return;
}

int result = version.compare(core.getVersion());
switch(result){
case -2 -> {
logger.warn("Encountered an exception while comparing versions. Are they valid?");
logger.warn("Own version: %s; New version: %s", core.getVersion(), version.getVersionNumber());
}
case -1 -> logger.info("You seem to run a newer version compared to Modrinth. Are you running a dev build?");
case 0 -> logger.info("No new update found. You're running the latest version!");
case 1 -> printUpdateBanner(version.getVersionNumber(), version.getId());
}
});
}, 0L, 12L, TimeUnit.HOURS);
}

public void disable(){
executor.shutdown();
try{
if(!executor.awaitTermination(1, TimeUnit.SECONDS)){
executor.shutdownNow();
if(!executor.awaitTermination(1, TimeUnit.SECONDS))
logger.warn("Scheduler didn't terminate in time!");
}
}catch(InterruptedException ex){
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}

private CompletableFuture<ModrinthVersion> checkUpdate(){
return CompletableFuture.supplyAsync(() -> {
PluginLogger logger = core.getPlugin().getPluginLogger();
if(core.getVersion().equals("UNKNOWN")){
logger.warn("Cannot perform Update check! Plugin version couldn't be parsed.");
return null;
}

Request request = new Request.Builder()
.url(String.format(url, loader))
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
.build();

try(Response response = client.newCall(request).execute()){
if(!response.isSuccessful()){
logger.warn("Encountered a non-successful response from Modrinth! Code: %d", response.code());
return null;
}

ResponseBody body = response.body();
if(body == null){
logger.warn("Received an empty Response body from Modrinth!");
return null;
}

String responseString = body.string();
if(responseString.isEmpty()){
logger.warn("Received an empty Response body from Modrinth!");
return null;
}

List<ModrinthVersion> list = gson.fromJson(responseString, listType);
if(list == null || list.isEmpty()){
logger.warn("Couldn't convert JSON Array into a valid list.");
return null;
}

ModrinthVersion version = list.get(0);
if(version.getVersionNumber() == null || version.getVersionNumber().isEmpty()){
logger.warn("Cannot check latest version. Received version number was null/empty.");
return null;
}

return version;
}catch(JsonSyntaxException ex){
logger.warn("Encountered invalid JSON from Response body!", ex);
return null;
}catch(IOException ex){
logger.warn("Encountered Exception while checking for an update!", ex);
return null;
}
});
}

private void printUpdateBanner(String version, String versionId){
logger.info("==================================================================");
logger.info("You are running an outdated version of AdvancedServerList!");
logger.info("");
logger.info("Your version: %s", core.getVersion());
logger.info("Modrinth version: %s", version);
logger.info("");
logger.info("You can download the latest release from here:");
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", versionId);
logger.info("==================================================================");
}

public static class ModrinthVersion{
private String id;
private String versionNumber;

public ModrinthVersion(String id, String versionNumber){
this.id = id;
this.versionNumber = versionNumber;
}

public String getId(){
return id;
}

public String getVersionNumber(){
return versionNumber;
}

public int compare(String version){
String[] oldParts = version.split("\\.");
String[] newParts = versionNumber.split("\\.");
int length = Math.max(oldParts.length, newParts.length);

for(int i = 0; i < length; i++){
try{
int oldPart = i < oldParts.length ? Integer.parseInt(oldParts[i]) : 0;
int newPart = i < newParts.length ? Integer.parseInt(newParts[i]) : 0;

if(oldPart < newPart)
return 1;

if(oldPart > newPart)
return -1;
}catch(NumberFormatException ex){
return -2;
}
}
return 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ public interface PluginCore<F>{
String getPlatformName();

String getPlatformVersion();

String getLoader();
}
10 changes: 9 additions & 1 deletion core/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ unknown_player: "Anonymous"
# When set to true will AdvancedServerList not load any players from the cache.data file (if present) nor save any to it.
# Placeholders that rely on the player being cached (such as ${player isWhitelisted}) will not work while this option is enabled.
#
disable_cache: false
disable_cache: false

#
# Should AdvancedServerList check for new versions?
#
# When set to true will AdvancedServerList check for a new version on modrinth.com every 12 hours and inform you in the
# console about whether it found any new update or not.
#
check_updates: true
Loading

0 comments on commit 6f06c47

Please sign in to comment.