3
0

Initial autoupdater

This commit is contained in:
Prototik
2015-05-31 18:21:26 +07:00
parent 8c7ff941cf
commit de86e3c9f6
16 changed files with 817 additions and 66 deletions

View File

@ -0,0 +1,48 @@
package kcauldron;
import java.io.File;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Properties;
public class KCauldron {
private static String sCurrentVersion;
public static String getCurrentVersion() {
if (sCurrentVersion != null)
return sCurrentVersion;
try {
Enumeration<URL> resources = KCauldron.class.getClassLoader()
.getResources("META-INF/MANIFEST.MF");
Properties manifest = new Properties();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
manifest.load(url.openStream());
String version = manifest.getProperty("KCauldron-Version");
if (version != null) {
String path = url.getPath();
String jarFilePath = path.substring(path.indexOf(":") + 1, path.indexOf("!"));
jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
sServerLocation = new File(jarFilePath);
return sCurrentVersion = version;
}
manifest.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
return sCurrentVersion = "UNKNOWN";
}
private static File sServerLocation;
public static File getServerLocation() {
getCurrentVersion();
return sServerLocation;
}
public static File sNewServerLocation;
public static String sNewServerVersion;
public static boolean sUpdateInProgress;
}

View File

@ -0,0 +1,71 @@
package kcauldron;
import kcauldron.updater.CommandSenderUpdateCallback;
import kcauldron.updater.KCauldronUpdater;
import kcauldron.updater.KVersionRetriever;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
public class KCauldronCommand extends Command {
public static final String NAME = "kcauldron";
public static final String CHECK = NAME + ".check";
public static final String UPDATE = NAME + ".update";
public KCauldronCommand() {
super(NAME);
StringBuilder builder = new StringBuilder();
builder.append(String.format("/%s check\n", NAME));
builder.append(String.format("/%s update [version]\n", NAME));
setUsage(builder.toString());
setPermission("kcauldron");
}
public boolean testPermission(CommandSender target, String permission) {
if (testPermissionSilent(target, permission)) {
return true;
}
target.sendMessage(ChatColor.RED
+ "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.");
return false;
}
public boolean testPermissionSilent(CommandSender target, String permission) {
if (!super.testPermissionSilent(target)) {
return false;
}
for (String p : permission.split(";"))
if (target.hasPermission(p))
return true;
return false;
}
@Override
public boolean execute(CommandSender sender, String commandLabel,
String[] args) {
if (!testPermission(sender))
return true;
if (args.length == 0) {
sender.sendMessage(ChatColor.YELLOW + "Please specify action");
return true;
}
String action = args[0];
if ("check".equals(action)) {
if (!testPermission(sender, CHECK))
return true;
sender.sendMessage(ChatColor.GREEN + "Initiated version check...");
new KVersionRetriever(new CommandSenderUpdateCallback(sender),
false);
} else if ("update".equals(action)) {
KCauldronUpdater.initUpdate(sender, args.length > 1 ? args[1]
: null);
} else {
sender.sendMessage(ChatColor.RED + "Unknown action");
}
return true;
}
}

View File

@ -0,0 +1,78 @@
package kcauldron;
import org.bukkit.configuration.file.YamlConfiguration;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.cauldron.configuration.BoolSetting;
import net.minecraftforge.cauldron.configuration.ConfigBase;
import net.minecraftforge.cauldron.configuration.Setting;
import net.minecraftforge.cauldron.configuration.StringSetting;
public class KCauldronConfig extends ConfigBase {
public BoolSetting commandEnable = new BoolSetting(this, "command.enable",
true, "Enable KCauldron command");
public BoolSetting updatecheckerEnable = new BoolSetting(this,
"updatechecker.enable", true, "Enable KCauldron update checker");
public BoolSetting updatecheckerDeleteOld = new BoolSetting(this,
"updatechecker.deleteOld", true, "Delete old version after update");
public StringSetting updatecheckerSymlinks = new StringSetting(this,
"updatechecker.symlinks", "", "(Re)create symlinks after update");
public BoolSetting updatecheckerAutoinstall = new BoolSetting(this,
"updatechecker.autoinstall", false, "Install updates without confirming");
public BoolSetting updatecheckerQuite = new BoolSetting(this,
"updatechecker.quite", false, "Print less info during update");
public StringSetting updatecheckerInstallAs = new StringSetting(this,
"updatechecker.installAs", "", "Install new version with specified name");
public KCauldronConfig() {
super("kcauldron.yml", "kc");
register(commandEnable);
register(updatecheckerEnable);
register(updatecheckerDeleteOld);
register(updatecheckerSymlinks);
register(updatecheckerAutoinstall);
register(updatecheckerQuite);
register(updatecheckerInstallAs);
load();
}
private void register(Setting<?> setting) {
settings.put(setting.path, setting);
}
@Override
public void registerCommands() {
if (commandEnable.getValue()) {
super.registerCommands();
}
}
@Override
protected void addCommands() {
commands.put(commandName, new KCauldronCommand());
}
@Override
protected void load() {
try {
config = YamlConfiguration.loadConfiguration(configFile);
String header = "";
for (Setting<?> toggle : settings.values()) {
if (!toggle.description.equals(""))
header += "Setting: " + toggle.path + " Default: "
+ toggle.def + " # " + toggle.description + "\n";
config.addDefault(toggle.path, toggle.def);
settings.get(toggle.path).setValue(
config.getString(toggle.path));
}
config.options().header(header);
config.options().copyDefaults(true);
save();
} catch (Exception ex) {
MinecraftServer.getServer().logSevere(
"Could not load " + this.configFile);
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,52 @@
package kcauldron;
import org.apache.logging.log4j.Level;
import cpw.mods.fml.common.FMLLog;
public class KLog {
private static final KLog DEFAULT_LOGGER = new KLog("KCauldron");
public static KLog get() {
return DEFAULT_LOGGER;
}
public static KLog get(String tag) {
return new KLog("KCauldron: " + tag);
}
private final String mTag;
public KLog(String tag) {
mTag = tag;
}
public void log(Level level, Throwable throwable, String message,
Object... args) {
Throwable t = null;
if (throwable != null) {
t = new Throwable();
t.initCause(throwable);
t.fillInStackTrace();
}
FMLLog.log(mTag, level, t, String.format(message, args));
}
public void warning(String message, Object... args) {
log(Level.WARN, null, message, args);
}
public void warning(Throwable throwable, String message,
Object... args) {
log(Level.WARN, throwable, message, args);
}
public void info(String message, Object... args) {
log(Level.INFO, null, message, args);
}
public void info(Throwable throwable, String message,
Object... args) {
log(Level.INFO, throwable, message, args);
}
}

View File

@ -0,0 +1,59 @@
package kcauldron.updater;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import kcauldron.updater.KVersionRetriever.IVersionCheckCallback;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
public class CommandSenderUpdateCallback implements IVersionCheckCallback {
private Reference<CommandSender> mSender;
public CommandSenderUpdateCallback(CommandSender sender) {
mSender = new WeakReference<CommandSender>(sender);
}
protected CommandSender getSender() {
return mSender.get();
}
@Override
public void upToDate(String version) {
CommandSender sender = mSender.get();
if (sender != null) {
sender.sendMessage(ChatColor.GREEN
+ "Running version of KCauldron is up-to-date: " + version);
}
DefaultUpdateCallback.INSTANCE.upToDate(version);
}
@Override
public void newVersion(String currentVersion, String newVersion) {
CommandSender sender = mSender.get();
if (sender != null) {
newVersion(sender, currentVersion, newVersion);
}
DefaultUpdateCallback.INSTANCE.newVersion(currentVersion, newVersion);
}
public static void newVersion(CommandSender sender, String currentVersion, String newVersion) {
sender.sendMessage(new String[] {
ChatColor.YELLOW + "Found new version of KCauldron: "
+ newVersion,
ChatColor.YELLOW + "Current is " + currentVersion,
ChatColor.YELLOW + "Type '" + ChatColor.BLUE
+ "/kc update" + ChatColor.YELLOW
+ "' to update" });
}
@Override
public void error(Throwable t) {
CommandSender sender = mSender.get();
if (sender != null) {
sender.sendMessage(ChatColor.RED
+ "Error ocurred durring version check, see details in server log");
}
}
}

View File

@ -0,0 +1,74 @@
package kcauldron.updater;
import kcauldron.KCauldron;
import kcauldron.KCauldronCommand;
import kcauldron.updater.KVersionRetriever.IVersionCheckCallback;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerJoinEvent;
public class DefaultUpdateCallback implements IVersionCheckCallback {
public static DefaultUpdateCallback INSTANCE;
static {
INSTANCE = new DefaultUpdateCallback();
}
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (mHasUpdate && hasPermission(player)) {
sendUpdate(player);
}
}
private boolean hasPermission(CommandSender player) {
return player.hasPermission(KCauldronCommand.UPDATE);
}
private void sendUpdate(CommandSender player) {
CommandSenderUpdateCallback.newVersion(player, mCurrentVersion,
mNewVersion);
}
private boolean mHasUpdate;
private String mCurrentVersion;
private String mNewVersion;
private DefaultUpdateCallback() {
}
@Override
public void upToDate(String version) {
mHasUpdate = false;
mCurrentVersion = version;
mNewVersion = null;
}
@Override
public void newVersion(String currentVersion, String newVersion) {
mCurrentVersion = currentVersion;
mNewVersion = newVersion;
if (!mHasUpdate) {
for (Player player : Bukkit.getOnlinePlayers()) {
if (hasPermission(player)) {
sendUpdate(player);
}
}
}
mHasUpdate = true;
if (MinecraftServer.kcauldronConfig.updatecheckerAutoinstall.getValue()
&& !mNewVersion.equals(KCauldron.sNewServerVersion)
&& !KCauldron.sUpdateInProgress) {
Bukkit.getConsoleSender().sendMessage("Triggering auto update");
KCauldronUpdater.initUpdate(Bukkit.getConsoleSender(), newVersion);
}
}
@Override
public void error(Throwable t) {
}
}

View File

@ -0,0 +1,186 @@
package kcauldron.updater;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import kcauldron.KCauldron;
import net.minecraft.server.MinecraftServer;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import com.google.common.base.Splitter;
public class KCauldronUpdater implements Runnable {
private static final class LatestVersionCallback extends
CommandSenderUpdateCallback {
public LatestVersionCallback(CommandSender sender) {
super(sender);
}
@Override
public void newVersion(String currentVersion, String newVersion) {
startUpdate(getSender(), newVersion);
}
@Override
public void upToDate(String version) {
CommandSender sender = getSender();
if (sender != null) {
sender.sendMessage(ChatColor.DARK_PURPLE + "Current version ("
+ version + ") is up to date");
}
}
}
public static void initUpdate(CommandSender sender, String version) {
if (KCauldron.sUpdateInProgress) {
sender.sendMessage(ChatColor.RED
+ "Update stopped: another update in progress");
return;
}
KCauldron.sUpdateInProgress = true;
if (version == null) {
sender.sendMessage(ChatColor.DARK_PURPLE
+ "Fetching latest version...");
new KVersionRetriever(new LatestVersionCallback(sender), false);
} else {
startUpdate(sender, version);
}
}
private static void startUpdate(CommandSender sender, String version) {
if (sender != null) {
sender.sendMessage(ChatColor.DARK_PURPLE + "Starting update to "
+ version + "...");
}
new KCauldronUpdater(sender, version);
}
private final CommandSender mSender;
private final String mVersion;
private final Thread mThread;
public KCauldronUpdater(CommandSender sender, String version) {
mSender = sender;
mVersion = version;
mThread = new Thread(this);
mThread.setName("KCauldron updater");
mThread.setPriority(Thread.MIN_PRIORITY);
mThread.start();
}
@Override
public void run() {
try {
boolean quite = MinecraftServer.kcauldronConfig.updatecheckerQuite
.getValue();
MinecraftServer server = MinecraftServer.getServer();
final String filename = "KCauldron-" + mVersion + "-server.jar";
File path = KCauldron.getServerLocation();
File newPath = new File(path.getParentFile(),
getInstallAs(filename));
if (mSender != null) {
if (!quite) {
mSender.sendMessage(ChatColor.DARK_PURPLE
+ "Current version is located in " + ChatColor.GOLD
+ path.getAbsolutePath());
mSender.sendMessage(ChatColor.DARK_PURPLE
+ "Installing new version in " + ChatColor.GOLD
+ newPath.getAbsolutePath());
}
if (newPath.exists()) {
Bukkit.getConsoleSender().sendMessage(
"ERROR: Install location already exists: "
+ newPath.getAbsolutePath());
mSender.sendMessage(ChatColor.RED
+ "ERROR: Install location already exists");
return;
}
if (!quite) {
mSender.sendMessage(ChatColor.DARK_PURPLE
+ "Downloading new version...");
}
}
HttpUriRequest request = RequestBuilder
.get()
.setUri("https://prok.pw/repo/pw/prok/KCauldron/"
+ mVersion + "/" + filename)
.addParameter("hostname", server.getHostname())
.addParameter("port", "" + server.getPort()).build();
HttpResponse response = HttpClientBuilder.create()
.setUserAgent("KCauldron Updater").build().execute(request);
InputStream is = response.getEntity().getContent();
Files.copy(is, newPath.toPath());
if (mSender != null && !quite) {
mSender.sendMessage(ChatColor.DARK_PURPLE
+ "Download completed");
}
makeSymlinks(newPath);
if (MinecraftServer.kcauldronConfig.updatecheckerDeleteOld
.getValue()) {
if (mSender != null && !quite) {
mSender.sendMessage(ChatColor.DARK_PURPLE
+ "Mark old version for deletion");
}
path.deleteOnExit();
if (KCauldron.sNewServerLocation != null) {
KCauldron.sNewServerLocation.deleteOnExit();
}
}
if (mSender != null) {
mSender.sendMessage(ChatColor.DARK_PURPLE + "Update completed");
}
KCauldron.sNewServerLocation = newPath;
KCauldron.sNewServerVersion = mVersion;
} catch (Exception e) {
e.printStackTrace();
if (mSender != null) {
mSender.sendMessage(ChatColor.RED + "Failed update to "
+ mVersion);
}
} finally {
KCauldron.sUpdateInProgress = false;
}
}
private String getInstallAs(String filename) {
String path = MinecraftServer.kcauldronConfig.updatecheckerInstallAs
.getValue().trim();
if (path.length() > 0) {
return path.replace("%version%", mVersion);
}
return filename;
}
private void makeSymlinks(File newPath) {
try {
for (String symlink : Splitter.on(File.pathSeparatorChar).split(
MinecraftServer.kcauldronConfig.updatecheckerSymlinks
.getValue())) {
symlink = symlink.trim();
if (symlink.length() == 0)
continue;
File symlinkPath = new File(symlink);
if (mSender != null
&& !MinecraftServer.kcauldronConfig.updatecheckerQuite
.getValue()) {
mSender.sendMessage(ChatColor.RED + "Create symlink "
+ ChatColor.GOLD + symlinkPath.getAbsolutePath());
}
if (symlinkPath.exists()) {
symlinkPath.delete();
}
Files.createSymbolicLink(symlinkPath.toPath(), newPath.toPath());
}
} catch (Exception e) {
}
}
}

View File

@ -0,0 +1,113 @@
package kcauldron.updater;
import java.io.InputStreamReader;
import java.lang.Thread.UncaughtExceptionHandler;
import kcauldron.KCauldron;
import kcauldron.KLog;
import net.minecraft.server.MinecraftServer;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class KVersionRetriever implements Runnable, UncaughtExceptionHandler {
private static final boolean DEBUG;
private static final KLog sLogger;
private static final JSONParser sParser;
private static final String sCurrentVersion;
private static MinecraftServer sServer;
static {
DEBUG = false;
sLogger = KLog.get(KVersionRetriever.class.getSimpleName());
sCurrentVersion = KCauldron.getCurrentVersion();
sParser = new JSONParser();
}
public static void init(MinecraftServer server) {
sServer = server;
if (MinecraftServer.kcauldronConfig.updatecheckerEnable.getValue()) {
new KVersionRetriever(DefaultUpdateCallback.INSTANCE, true);
}
}
private final IVersionCheckCallback mCallback;
private final boolean mLoop;
private final Thread mThread;
public KVersionRetriever(IVersionCheckCallback callback, boolean loop) {
if (DEBUG)
sLogger.info("Created new version retrivier");
mCallback = callback;
mLoop = loop;
mThread = new Thread(this);
mThread.setName("KCauldron version retrievier");
mThread.setPriority(Thread.MIN_PRIORITY);
mThread.setDaemon(true);
mThread.setUncaughtExceptionHandler(this);
mThread.start();
}
@Override
public void run() {
while (!mThread.isInterrupted()) {
check();
if (!mLoop)
break;
try {
Thread.sleep(1000 * 60 * 10);// Sleep ten minutes
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void check() {
if (DEBUG)
sLogger.info("Requesting for new version...");
try {
HttpUriRequest request = RequestBuilder.get()
.setUri("https://prok.pw/version/pw.prok/KCauldron")
.addParameter("hostname", sServer.getHostname())
.addParameter("port", "" + sServer.getPort()).build();
HttpResponse response = HttpClientBuilder.create()
.setUserAgent("KCauldron Version Retriever").build()
.execute(request);
JSONObject json = (JSONObject) sParser.parse(new InputStreamReader(
response.getEntity().getContent()));
String version = (String) json.get("version");
if (DEBUG) {
sLogger.info("Got the latest version: %s", version);
sLogger.info("Current version is %s", sCurrentVersion);
}
if (!sCurrentVersion.equals(version)) {
mCallback.newVersion(sCurrentVersion, version);
} else {
mCallback.upToDate(version);
}
} catch (Exception e) {
uncaughtException(null, e);
}
}
@Override
public void uncaughtException(Thread t, Throwable e) {
sLogger.warning(e, "Error occured during retriving version");
if (mCallback != null) {
mCallback.error(e);
}
}
public interface IVersionCheckCallback {
void upToDate(String version);
void newVersion(String currentVersion, String newVersion);
void error(Throwable t);
}
}

View File

@ -97,9 +97,9 @@ public class VersionInfo {
private void doFileExtract(File path) throws IOException
{
InputStream inputStream = getClass().getResourceAsStream("/"+getContainedFile());
OutputSupplier<FileOutputStream> outputSupplier = Files.newOutputStreamSupplier(path);
System.out.println("doFileExtract path = " + path.getAbsolutePath() + ", inputStream = " + inputStream + ", outputSupplier = " + outputSupplier);
ByteStreams.copy(inputStream, outputSupplier);
FileOutputStream outputStream = new FileOutputStream(path);
System.out.println("doFileExtract path = " + path.getAbsolutePath() + ", inputStream = " + inputStream + ", outputStream = " + outputStream);
ByteStreams.copy(inputStream, outputStream);
}
public static String getMinecraftVersion()

View File

@ -2,6 +2,7 @@ package net.minecraftforge.cauldron.configuration;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.cauldron.command.CauldronCommand;
import org.bukkit.configuration.file.YamlConfiguration;
public class CauldronConfig extends ConfigBase

View File

@ -0,0 +1,23 @@
package net.minecraftforge.cauldron.configuration;
public class StringSetting extends Setting<String> {
private String value;
private ConfigBase config;
public StringSetting(ConfigBase config, String path, String def,
String description) {
super(path, def, description);
this.value = def;
this.config = config;
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
config.set(path, this.value = value);
}
}