diff --git a/build.gradle b/build.gradle index 8360abd1..3a645164 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ jar { minecraft { version = config.minecraft_version + "-" + config.forge_version + replaceIn "AEConfig.java" replace "@version@", project.version replace "@aechannel@", config.aechannel diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index 8c7d6633..f25d3ba8 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -55,9 +55,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public static final double TUNNEL_POWER_LOSS = 0.05; - public String latestVersion = VERSION; - public long latestTimeStamp = 0; - public static final String VERSION = "@version@"; public static final String CHANNEL = "@aechannel@"; @@ -149,7 +146,7 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public boolean disableColoredCableRecipesInNEI = true; public boolean updatable = false; - final private File myPath; + final private File configFile; public double meteoriteClusterChance = 0.1; public double meteoriteSpawnChance = 0.3; @@ -224,22 +221,20 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public String getFilePath() { - return this.myPath.toString(); + return this.configFile.toString(); } - public AEConfig(String path) { - super( new File( path + "AppliedEnergistics2.cfg" ) ); - this.myPath = new File( path + "AppliedEnergistics2.cfg" ); + public AEConfig( File configFile ) { + super( configFile ); + this.configFile = configFile; FMLCommonHandler.instance().bus().register( this ); - final double DEFAULT_BC_EXCHANGE = 5.0; final double DEFAULT_IC2_EXCHANGE = 2.0; final double DEFAULT_RTC_EXCHANGE = 1.0 / 11256.0; final double DEFAULT_RF_EXCHANGE = 0.5; final double DEFAULT_MEKANISM_EXCHANGE = 0.2; - PowerUnits.MJ.conversionRatio = this.get( "PowerRatios", "BuildCraft", DEFAULT_BC_EXCHANGE ).getDouble( DEFAULT_BC_EXCHANGE ); PowerUnits.MK.conversionRatio = this.get( "PowerRatios", "Mekanism", DEFAULT_MEKANISM_EXCHANGE ).getDouble( DEFAULT_MEKANISM_EXCHANGE ); PowerUnits.EU.conversionRatio = this.get( "PowerRatios", "IC2", DEFAULT_IC2_EXCHANGE ).getDouble( DEFAULT_IC2_EXCHANGE ); PowerUnits.WA.conversionRatio = this.get( "PowerRatios", "RotaryCraft", DEFAULT_RTC_EXCHANGE ).getDouble( DEFAULT_RTC_EXCHANGE ); @@ -340,19 +335,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon this.craftingCalculationTimePerTick ); } - if ( this.isFeatureEnabled( AEFeature.VersionChecker ) ) - { - try - { - this.latestVersion = this.get( "VersionChecker", "LatestVersion", "" ).getString(); - this.latestTimeStamp = Long.parseLong( this.get( "VersionChecker", "LatestTimeStamp", "" ).getString() ); - } - catch (NumberFormatException err) - { - this.latestTimeStamp = 0; - } - } - this.updatable = true; } @@ -411,12 +393,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon @Override public void save() { - if ( this.isFeatureEnabled( AEFeature.VersionChecker ) ) - { - this.get( "VersionChecker", "LatestVersion", this.latestVersion ).set( this.latestVersion ); - this.get( "VersionChecker", "LatestTimeStamp", "" ).set( Long.toString( this.latestTimeStamp ) ); - } - if ( this.isFeatureEnabled( AEFeature.SpatialIO ) ) { this.get( "spatialio", "storageBiomeID", this.storageBiomeID ).set( this.storageBiomeID ); diff --git a/src/main/java/appeng/core/AppEng.java b/src/main/java/appeng/core/AppEng.java index 9992320e..97699dc8 100644 --- a/src/main/java/appeng/core/AppEng.java +++ b/src/main/java/appeng/core/AppEng.java @@ -22,8 +22,6 @@ package appeng.core; import java.io.File; import java.util.concurrent.TimeUnit; -import com.google.common.base.Stopwatch; - import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.Mod; @@ -37,6 +35,8 @@ import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.event.FMLServerStoppingEvent; import cpw.mods.fml.common.network.NetworkRegistry; +import com.google.common.base.Stopwatch; + import appeng.core.crash.CrashEnhancement; import appeng.core.crash.CrashInfo; import appeng.core.features.AEFeature; @@ -47,6 +47,7 @@ import appeng.integration.IntegrationRegistry; import appeng.integration.IntegrationType; import appeng.server.AECommand; import appeng.services.VersionChecker; +import appeng.services.version.VersionCheckerConfig; import appeng.util.Platform; @@ -70,7 +71,7 @@ public class AppEng private final IMCHandler imcHandler; - private String configPath; + private File configDirectory; public AppEng() { @@ -81,9 +82,9 @@ public class AppEng FMLCommonHandler.instance().registerCrashCallable( new CrashEnhancement( CrashInfo.MOD_VERSION ) ); } - public String getConfigPath() + public final File getConfigDirectory() { - return this.configPath; + return this.configDirectory; } public boolean isIntegrationEnabled( IntegrationType integrationName ) @@ -104,11 +105,16 @@ public class AppEng CommonHelper.proxy.missingCoreMod(); } - Stopwatch star = Stopwatch.createStarted(); - this.configPath = event.getModConfigurationDirectory().getPath() + File.separator + "AppliedEnergistics2" + File.separator; + Stopwatch watch = Stopwatch.createStarted(); + this.configDirectory = new File(event.getModConfigurationDirectory().getPath(), "AppliedEnergistics2"); - AEConfig.instance = new AEConfig( this.configPath ); - FacadeConfig.instance = new FacadeConfig( this.configPath ); + final File configFile = new File( this.configDirectory, "AppliedEnergistics2.cfg"); + final File facadeFile = new File( this.configDirectory, "Facades.cfg" ); + final File versionFile = new File( this.configDirectory, "VersionChecker.cfg" ); + + AEConfig.instance = new AEConfig( configFile ); + FacadeConfig.instance = new FacadeConfig( facadeFile ); + final VersionCheckerConfig versionCheckerConfig = new VersionCheckerConfig( versionFile ); AELog.info( "Pre Initialization ( started )" ); @@ -121,19 +127,23 @@ public class AppEng Registration.INSTANCE.preInitialize( event ); - if ( AEConfig.instance.isFeatureEnabled( AEFeature.VersionChecker ) ) + if ( versionCheckerConfig.isEnabled() ) { - AELog.info( "Starting VersionChecker" ); - this.startService( "AE2 VersionChecker", new Thread( new VersionChecker() ) ); + final VersionChecker versionChecker = new VersionChecker( versionCheckerConfig ); + final Thread versionCheckerThread = new Thread( versionChecker ); + + this.startService( "AE2 VersionChecker", versionCheckerThread ); } - AELog.info( "Pre Initialization ( ended after " + star.elapsed( TimeUnit.MILLISECONDS ) + "ms )" ); + AELog.info( "Pre Initialization ( ended after " + watch.elapsed( TimeUnit.MILLISECONDS ) + "ms )" ); } private void startService( String serviceName, Thread thread ) { thread.setName( serviceName ); thread.setPriority( Thread.MIN_PRIORITY ); + + AELog.info( "Starting " + serviceName ); thread.start(); } diff --git a/src/main/java/appeng/core/FacadeConfig.java b/src/main/java/appeng/core/FacadeConfig.java index 62f35477..a2025f8e 100644 --- a/src/main/java/appeng/core/FacadeConfig.java +++ b/src/main/java/appeng/core/FacadeConfig.java @@ -35,8 +35,8 @@ public class FacadeConfig extends Configuration public static FacadeConfig instance; final Pattern replacementPattern; - public FacadeConfig(String path) { - super( new File( path + "Facades.cfg" ) ); + public FacadeConfig( File facadeFile ) { + super( facadeFile ); this.replacementPattern = Pattern.compile( "[^a-zA-Z0-9]" ); } diff --git a/src/main/java/appeng/core/Registration.java b/src/main/java/appeng/core/Registration.java index c131f7ac..109251f6 100644 --- a/src/main/java/appeng/core/Registration.java +++ b/src/main/java/appeng/core/Registration.java @@ -21,10 +21,6 @@ package appeng.core; import java.lang.reflect.Field; -import com.google.common.base.Optional; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - import net.minecraft.item.crafting.CraftingManager; import net.minecraft.util.WeightedRandomChestContent; import net.minecraft.world.biome.BiomeGenBase; @@ -41,6 +37,10 @@ import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.VillagerRegistry; +import com.google.common.base.Optional; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + import appeng.api.AEApi; import appeng.api.IAppEngApi; import appeng.api.config.Upgrades; @@ -546,7 +546,7 @@ public final class Registration ItemMultiMaterial.instance.makeUnique(); if ( AEConfig.instance.isFeatureEnabled( AEFeature.CustomRecipes ) ) - this.recipeHandler.parseRecipes( new ConfigLoader( AppEng.instance.getConfigPath() ), "index.recipe" ); + this.recipeHandler.parseRecipes( new ConfigLoader( AppEng.instance.getConfigDirectory() ), "index.recipe" ); else this.recipeHandler.parseRecipes( new JarLoader( "/assets/appliedenergistics2/recipes/" ), "index.recipe" ); diff --git a/src/main/java/appeng/core/features/AEFeature.java b/src/main/java/appeng/core/features/AEFeature.java index 2600f9cb..2990e85b 100644 --- a/src/main/java/appeng/core/features/AEFeature.java +++ b/src/main/java/appeng/core/features/AEFeature.java @@ -62,7 +62,7 @@ public enum AEFeature MassCannonBlockDamage("BlockFeatures"), TinyTNTBlockDamage("BlockFeatures"), Facades("Facades"), - VersionChecker("Services"), UnsupportedDeveloperTools("Misc", false), Creative("Misc"), + UnsupportedDeveloperTools("Misc", false), Creative("Misc"), GrinderLogging("Misc", false), Logging("Misc"), IntegrationLogging("Misc", false), CustomRecipes("Crafting", false), WebsiteRecipes("Misc", false), diff --git a/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java b/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java index dd790ae7..be765a07 100644 --- a/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java +++ b/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java @@ -29,7 +29,7 @@ import appeng.core.WorldSettings; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; import appeng.core.sync.network.NetworkHandler; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.ICompassCallback; public class PacketCompassRequest extends AppEngPacket implements ICompassCallback { diff --git a/src/main/java/appeng/hooks/MeteoriteWorldGen.java b/src/main/java/appeng/hooks/MeteoriteWorldGen.java index f0ccedd9..e8c81fb8 100644 --- a/src/main/java/appeng/hooks/MeteoriteWorldGen.java +++ b/src/main/java/appeng/hooks/MeteoriteWorldGen.java @@ -33,7 +33,7 @@ import appeng.core.AEConfig; import appeng.core.WorldSettings; import appeng.core.features.registries.WorldGenRegistry; import appeng.helpers.MeteoritePlacer; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.ICompassCallback; import appeng.util.Platform; final public class MeteoriteWorldGen implements IWorldGenerator diff --git a/src/main/java/appeng/recipes/loader/ConfigLoader.java b/src/main/java/appeng/recipes/loader/ConfigLoader.java index 19ee7a6d..0fb2aa36 100644 --- a/src/main/java/appeng/recipes/loader/ConfigLoader.java +++ b/src/main/java/appeng/recipes/loader/ConfigLoader.java @@ -18,6 +18,7 @@ package appeng.recipes.loader; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -25,19 +26,21 @@ import java.io.InputStreamReader; import appeng.api.recipes.IRecipeLoader; -public class ConfigLoader implements IRecipeLoader + +public final class ConfigLoader implements IRecipeLoader { + private final File rootDirectory; - private final String rootPath; - - public ConfigLoader(String s) { - this.rootPath = s; + public ConfigLoader( File rootDirectory ) + { + this.rootDirectory = rootDirectory; } @Override - public BufferedReader getFile(String s) throws Exception + public BufferedReader getFile( String s ) throws Exception { - File f = new File( this.rootPath + s ); + final File f = new File( this.rootDirectory, s ); + return new BufferedReader( new InputStreamReader( new FileInputStream( f ), "UTF-8" ) ); } } diff --git a/src/main/java/appeng/services/CompassService.java b/src/main/java/appeng/services/CompassService.java index 41a7a69a..24760d07 100644 --- a/src/main/java/appeng/services/CompassService.java +++ b/src/main/java/appeng/services/CompassService.java @@ -32,8 +32,8 @@ import net.minecraft.world.chunk.Chunk; import appeng.api.AEApi; import appeng.api.util.DimensionalCoord; -import appeng.services.helpers.CompassReader; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.CompassReader; +import appeng.services.compass.ICompassCallback; public class CompassService implements ThreadFactory { diff --git a/src/main/java/appeng/services/VersionChecker.java b/src/main/java/appeng/services/VersionChecker.java index 3fbfc372..7aa9fc73 100644 --- a/src/main/java/appeng/services/VersionChecker.java +++ b/src/main/java/appeng/services/VersionChecker.java @@ -19,133 +19,167 @@ package appeng.services; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; import java.util.Date; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - import net.minecraft.nbt.NBTTagCompound; +import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.event.FMLInterModComms; import appeng.core.AEConfig; import appeng.core.AELog; import appeng.core.AppEng; +import appeng.services.version.github.FormattedRelease; +import appeng.services.version.github.ReleaseFetcher; +import appeng.services.version.ModVersionFetcher; +import appeng.services.version.Version; +import appeng.services.version.VersionCheckerConfig; +import appeng.services.version.VersionFetcher; +import appeng.services.version.VersionParser; -public class VersionChecker implements Runnable + +/** + * Tries to connect to GitHub to retrieve the most current build. + * After comparison with the local version, several path can be chosen. + * + * If the local version is invalid, somebody might have build that version themselves + * or it is run in a developer environment, then nothing needs to be done. + * + * If GitHub can not be reached, then either is GitHub down + * or the connection to GitHub disturbed, then nothing needs to be done, + * since no comparison can be reached + * + * If the version was just recently checked, then no need to poll again. + * Nobody wants to bother to update several times a day. + * + * Config enables to fine-tune when a version is considered newer + * + * If the local version is newer or equal to the GitHub version, + * then no update needs to be posted + * + * Only after all that cases, if the external version is higher than the local, + * use Version Checker Mod and post several information needed for it to update the mod. + */ +public final class VersionChecker implements Runnable { - private static final int FOUR_HOURS = 1000 * 3600 * 4; + private static final int SEC_TO_HOUR = 3600; + private static final int MS_TO_SEC = 1000; + private final VersionCheckerConfig config; - private final long delay; - - public VersionChecker() + public VersionChecker( VersionCheckerConfig config ) { - final long now = new Date().getTime(); - final long timeDiff = now - AEConfig.instance.latestTimeStamp; - - this.delay = Math.max( 1, FOUR_HOURS - timeDiff); + this.config = config; } @Override public void run() { - try + Thread.yield(); + + final String rawLastCheck = this.config.lastCheck(); + final long lastCheck = Long.parseLong( rawLastCheck ); + final Date now = new Date(); + final long nowInMs = now.getTime(); + final long intervalInMs = this.config.interval() * SEC_TO_HOUR * MS_TO_SEC; + final long lastAfterInterval = lastCheck + intervalInMs; + + this.processInterval( nowInMs, lastAfterInterval ); + + AELog.info( "Stopping AE2 VersionChecker" ); + } + + /** + * checks if enough time since last check has expired + * + * @param nowInMs now in milli seconds + * @param lastAfterInterval last version check including the interval defined in the config + */ + private void processInterval( long nowInMs, long lastAfterInterval ) + { + if ( nowInMs > lastAfterInterval ) { - this.sleep( this.delay ); + final String rawModVersion = AEConfig.VERSION; + final VersionParser parser = new VersionParser(); + final VersionFetcher modFetcher = new ModVersionFetcher( rawModVersion, parser ); + final ReleaseFetcher githubFetcher = new ReleaseFetcher( this.config, parser ); + + final Version modVersion = modFetcher.get(); + final FormattedRelease githubRelease = githubFetcher.get(); + + this.processVersions( modVersion, githubRelease ); } - catch (InterruptedException e) + else { - // :( - } - - while (true) - { - Thread.yield(); - - try - { - String MCVersion = cpw.mods.fml.common.Loader.instance().getMCVersionString().replace( "Minecraft ", "" ); - URL url = new URL( "http://feeds.ae-mod.info/latest.json?VersionMC=" + MCVersion + "&Channel=" + AEConfig.CHANNEL + "&CurrentVersion=" - + AEConfig.VERSION ); - - URLConnection yc = url.openConnection(); - yc.setRequestProperty( "User-Agent", "AE2/" + AEConfig.VERSION + " (Channel:" + AEConfig.CHANNEL + ',' + MCVersion.replace( " ", ":" ) + ')' ); - BufferedReader in = new BufferedReader( new InputStreamReader( yc.getInputStream() ) ); - - StringBuilder Version = new StringBuilder(); - String inputLine; - - while ((inputLine = in.readLine()) != null) - Version.append( inputLine ); - - in.close(); - - if ( Version.length() > 2 ) - { - JsonElement element = (new JsonParser()).parse( Version.toString() ); - - int version = element.getAsJsonObject().get( "FormatVersion" ).getAsInt(); - if ( version == 1 ) - { - JsonObject Meta = element.getAsJsonObject().get( "Meta" ).getAsJsonObject(); - JsonArray Versions = element.getAsJsonObject().get( "Versions" ).getAsJsonArray(); - if ( Versions.size() > 0 ) - { - JsonObject Latest = Versions.get( 0 ).getAsJsonObject(); - - AEConfig.instance.latestVersion = Latest.get( "Version" ).getAsString(); - AEConfig.instance.latestTimeStamp = (new Date()).getTime(); - AEConfig.instance.save(); - - if ( !AEConfig.VERSION.equals( AEConfig.instance.latestVersion ) ) - { - NBTTagCompound versionInf = new NBTTagCompound(); - versionInf.setString( "modDisplayName", "Applied Energistics 2" ); - versionInf.setString( "oldVersion", AEConfig.VERSION ); - versionInf.setString( "newVersion", AEConfig.instance.latestVersion ); - versionInf.setString( "updateUrl", Latest.get( "UserBuild" ).getAsString() ); - versionInf.setBoolean( "isDirectLink", true ); - - JsonElement changeLog = Latest.get( "ChangeLog" ); - if ( changeLog == null ) - versionInf.setString( "changeLog", "For full change log please see: " + Meta.get( "DownloadLink" ).getAsString() ); - else - versionInf.setString( "changeLog", changeLog.getAsString() ); - - versionInf.setString( "newFileName", "appliedenergistics2-" + AEConfig.instance.latestVersion + ".jar" ); - FMLInterModComms.sendRuntimeMessage( AppEng.instance, "VersionChecker", "addUpdate", versionInf ); - - AELog.info( "Stopping VersionChecker" ); - return; - } - } - } - } - - this.sleep( FOUR_HOURS ); - } - catch (Exception e) - { - try - { - this.sleep( FOUR_HOURS ); - } - catch (InterruptedException e1) - { - AELog.error( e ); - } - } + AELog.info( "Last check was just recently." ); } } - private void sleep(long i) throws InterruptedException + /** + * Checks if the retrieved version is newer as the current mod version. + * Will notify player if config is enabled. + * + * @param modVersion version of mod + * @param githubRelease release retrieved through github + */ + private void processVersions( Version modVersion, FormattedRelease githubRelease ) { - Thread.sleep( i ); + final Version githubVersion = githubRelease.version(); + final String modFormatted = modVersion.formatted(); + final String ghFormatted = githubVersion.formatted(); + + if ( githubVersion.isNewerAs( modVersion ) ) + { + final String changelog = githubRelease.changelog(); + + if ( this.config.shouldNotifyPlayer() ) + { + AELog.info( "Newer version is available: " + ghFormatted + " (found) > " + modFormatted + " (current)" ); + + if ( this.config.shouldPostChangelog() ) + { + AELog.info( "Changelog: " + changelog ); + } + } + + this.interactWithVersionCheckerMod( modFormatted, ghFormatted, changelog ); + } + else + { + AELog.info( "No newer version is available: " + ghFormatted + "(found) < " + modFormatted + " (current)"); + } + } + + /** + * Checks if the version checker mod is installed and handles it depending on that information + * + * @param modFormatted mod version formatted as rv2-beta-8 + * @param ghFormatted retrieved github version formatted as rv2-beta-8 + * @param changelog retrieved github changelog + */ + private void interactWithVersionCheckerMod( String modFormatted, String ghFormatted, String changelog ) + { + if ( Loader.isModLoaded( "VersionChecker" ) ) + { + final NBTTagCompound versionInf = new NBTTagCompound(); + versionInf.setString( "modDisplayName", AppEng.MOD_NAME ); + versionInf.setString( "oldVersion", modFormatted ); + versionInf.setString( "newVersion", ghFormatted ); + versionInf.setString( "updateUrl", "http://ae-mod.info/builds/appliedenergistics2-" + ghFormatted + ".jar" ); + versionInf.setBoolean( "isDirectLink", true ); + + if ( !changelog.isEmpty() ) + { + versionInf.setString( "changeLog", changelog ); + } + + versionInf.setString( "newFileName", "appliedenergistics2-" + ghFormatted + ".jar" ); + FMLInterModComms.sendRuntimeMessage( AppEng.instance, "VersionChecker", "addUpdate", versionInf ); + + AELog.info( "Reported new version to VersionChecker mod." ); + } + else + { + AELog.info( "VersionChecker mod is not installed; Proceeding." ); + } } } diff --git a/src/main/java/appeng/services/helpers/CompassException.java b/src/main/java/appeng/services/compass/CompassException.java similarity index 96% rename from src/main/java/appeng/services/helpers/CompassException.java rename to src/main/java/appeng/services/compass/CompassException.java index 9ddf4991..55c9453a 100644 --- a/src/main/java/appeng/services/helpers/CompassException.java +++ b/src/main/java/appeng/services/compass/CompassException.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; public class CompassException extends RuntimeException { diff --git a/src/main/java/appeng/services/helpers/CompassReader.java b/src/main/java/appeng/services/compass/CompassReader.java similarity index 98% rename from src/main/java/appeng/services/helpers/CompassReader.java rename to src/main/java/appeng/services/compass/CompassReader.java index 000454cc..3d3b3cbf 100644 --- a/src/main/java/appeng/services/helpers/CompassReader.java +++ b/src/main/java/appeng/services/compass/CompassReader.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; import java.io.File; import java.util.HashMap; diff --git a/src/main/java/appeng/services/helpers/CompassRegion.java b/src/main/java/appeng/services/compass/CompassRegion.java similarity index 99% rename from src/main/java/appeng/services/helpers/CompassRegion.java rename to src/main/java/appeng/services/compass/CompassRegion.java index 08e30c9e..691eb417 100644 --- a/src/main/java/appeng/services/helpers/CompassRegion.java +++ b/src/main/java/appeng/services/compass/CompassRegion.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; import java.io.File; diff --git a/src/main/java/appeng/services/helpers/ICompassCallback.java b/src/main/java/appeng/services/compass/ICompassCallback.java similarity index 97% rename from src/main/java/appeng/services/helpers/ICompassCallback.java rename to src/main/java/appeng/services/compass/ICompassCallback.java index 0e2c3266..e1f6cdeb 100644 --- a/src/main/java/appeng/services/helpers/ICompassCallback.java +++ b/src/main/java/appeng/services/compass/ICompassCallback.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; public interface ICompassCallback { diff --git a/src/main/java/appeng/services/version/BaseVersion.java b/src/main/java/appeng/services/version/BaseVersion.java new file mode 100644 index 00000000..53625e76 --- /dev/null +++ b/src/main/java/appeng/services/version/BaseVersion.java @@ -0,0 +1,91 @@ +package appeng.services.version; + + +/** + * Base version of {@link Version}. + * + * Provides a unified way to test for equality and print a formatted string + */ +public abstract class BaseVersion implements Version +{ + private final int revision; + private final Channel channel; + private final int build; + + /** + * @param revision revision in natural number + * @param channel channel + * @param build build in natural number + * + * @throws AssertionError if assertion are enabled and revision or build are not natural numbers + */ + public BaseVersion( int revision, Channel channel, int build ) + { + assert revision >= 0; + assert build >= 0; + + this.revision = revision; + this.channel = channel; + this.build = build; + } + + @Override + public final int revision() + { + return this.revision; + } + + @Override + public final Channel channel() + { + return this.channel; + } + + @Override + public final int build() + { + return this.build; + } + + @Override + public String formatted() + { + return "rv" + this.revision + '-' + this.channel.name().toLowerCase() + '-' + this.build; + } + + @Override + public final int hashCode() + { + int result = this.revision; + result = 31 * result + ( this.channel != null ? this.channel.hashCode() : 0 ); + result = 31 * result + this.build; + return result; + } + + @Override + public final boolean equals( Object o ) + { + if ( this == o ) + return true; + if ( !( o instanceof Version ) ) + return false; + + Version that = (Version) o; + + if ( this.revision != that.revision() ) + return false; + if ( this.build != that.build() ) + return false; + return this.channel == that.channel(); + } + + @Override + public final String toString() + { + return "Version{" + + "revision=" + this.revision + + ", channel=" + this.channel + + ", build=" + this.build + + '}'; + } +} diff --git a/src/main/java/appeng/services/version/Channel.java b/src/main/java/appeng/services/version/Channel.java new file mode 100644 index 00000000..6b37b5ea --- /dev/null +++ b/src/main/java/appeng/services/version/Channel.java @@ -0,0 +1,13 @@ +package appeng.services.version; + + +/** + * Represents the release channel of Applied Energistics. The mod is either in Alpha, Beta or Release channel. + * Any more might be confusing to the end-user + */ +public enum Channel +{ + Alpha, + Beta, + Release +} diff --git a/src/main/java/appeng/services/version/DefaultVersion.java b/src/main/java/appeng/services/version/DefaultVersion.java new file mode 100644 index 00000000..5dcaa890 --- /dev/null +++ b/src/main/java/appeng/services/version/DefaultVersion.java @@ -0,0 +1,35 @@ +package appeng.services.version; + + +/** + * AE prints version like rv2-beta-8 + * GitHub prints version like rv2.beta.8 + */ +public final class DefaultVersion extends BaseVersion +{ + /** + * @param revision natural number + * @param channel either alpha, beta or release + * @param build natural number + */ + public DefaultVersion( int revision, Channel channel, int build ) + { + super( revision, channel, build ); + } + + @Override + public boolean isNewerAs( Version maybeOlder ) + { + if ( this.revision() > maybeOlder.revision() ) + { + return true; + } + + if ( this.channel().compareTo( maybeOlder.channel() ) > 0 ) + { + return true; + } + + return this.build() > maybeOlder.build(); + } +} diff --git a/src/main/java/appeng/services/version/DoNotCheckVersion.java b/src/main/java/appeng/services/version/DoNotCheckVersion.java new file mode 100644 index 00000000..c92f43d6 --- /dev/null +++ b/src/main/java/appeng/services/version/DoNotCheckVersion.java @@ -0,0 +1,25 @@ +package appeng.services.version; + + +/** + * Exceptional template for {@link Version}, when the mod does not want a check + */ +public final class DoNotCheckVersion extends BaseVersion +{ + public DoNotCheckVersion() + { + super( Integer.MAX_VALUE, Channel.Release, Integer.MAX_VALUE ); + } + + @Override + public boolean isNewerAs( Version maybeOlder ) + { + return true; + } + + @Override + public String formatted() + { + return "dev build"; + } +} diff --git a/src/main/java/appeng/services/version/MissingVersion.java b/src/main/java/appeng/services/version/MissingVersion.java new file mode 100644 index 00000000..bf0a6298 --- /dev/null +++ b/src/main/java/appeng/services/version/MissingVersion.java @@ -0,0 +1,30 @@ +package appeng.services.version; + + +/** + * Exceptional template when the {@link Version} could not be retrieved + */ +public final class MissingVersion extends BaseVersion +{ + public MissingVersion() + { + super( 0, Channel.Alpha, 0 ); + } + + /** + * @param maybeOlder ignored + * + * @return false + */ + @Override + public boolean isNewerAs( Version maybeOlder ) + { + return false; + } + + @Override + public String formatted() + { + return "missing"; + } +} diff --git a/src/main/java/appeng/services/version/ModVersionFetcher.java b/src/main/java/appeng/services/version/ModVersionFetcher.java new file mode 100644 index 00000000..d5eb22a0 --- /dev/null +++ b/src/main/java/appeng/services/version/ModVersionFetcher.java @@ -0,0 +1,39 @@ +package appeng.services.version; + + +/** + * Wrapper for {@link VersionParser} to check if the check is happening in developer environment or in a pull request. + * + * In that case ignore the check. + */ +public final class ModVersionFetcher implements VersionFetcher +{ + private final String rawModVersion; + private final VersionParser parser; + + public ModVersionFetcher( String rawModVersion, VersionParser parser ) + { + this.rawModVersion = rawModVersion; + this.parser = parser; + } + + /** + * Parses only, if not checked in developer environment or in a pull request + * + * @return {@link DoNotCheckVersion} if in developer environment or pull request, else the parsed {@link Version} + */ + @Override + public Version get() + { + if ( this.rawModVersion.equals( "@version@" ) || this.rawModVersion.contains( "pr" ) ) + { + return new DoNotCheckVersion(); + } + else + { + final Version version = this.parser.parse( this.rawModVersion ); + + return version; + } + } +} diff --git a/src/main/java/appeng/services/version/Version.java b/src/main/java/appeng/services/version/Version.java new file mode 100644 index 00000000..94f2ae60 --- /dev/null +++ b/src/main/java/appeng/services/version/Version.java @@ -0,0 +1,42 @@ +package appeng.services.version; + + +/** + * Stores version information, which are easily compared + */ +public interface Version +{ + /** + * @return revision of this version + */ + int revision(); + + /** + * @return channel of this version + */ + Channel channel(); + + /** + * @return build of this version + */ + int build(); + + /** + * A version is never if these criteria are met: + * if the current revision is higher than the compared revision OR + * if revision are equal and the current channel is higher than the compared channel (Release > Beta > Alpha) OR + * if revision, channel are equal and the build is higher than the compared build + * + * @return true if criteria are met + */ + boolean isNewerAs( Version maybeOlder ); + + /** + * Prints the revision, channel and build into a common displayed way + * + * rv2-beta-8 + * + * @return formatted version + */ + String formatted(); +} diff --git a/src/main/java/appeng/services/version/VersionCheckerConfig.java b/src/main/java/appeng/services/version/VersionCheckerConfig.java new file mode 100644 index 00000000..9688972f --- /dev/null +++ b/src/main/java/appeng/services/version/VersionCheckerConfig.java @@ -0,0 +1,94 @@ +package appeng.services.version; + + +import java.io.File; +import java.util.Date; + +import net.minecraftforge.common.config.Configuration; + + +/** + * Seperate config file to handle the version checker + */ +public final class VersionCheckerConfig +{ + private static final int DEFAULT_INTERVAL_HOURS = 24; + private static final int MIN_INTERVAL_HOURS = 0; + private static final int MAX_INTERVAL_HOURS = 7 * 24; + + private final Configuration config; + + private final boolean isEnabled; + + private final String lastCheck; + private final int interval; + + private final String level; + + private final boolean shouldNotifyPlayer; + private final boolean shouldPostChangelog; + + /** + * @param file requires fully qualified file in which the config is saved + */ + public VersionCheckerConfig( File file ) + { + this.config = new Configuration( file ); + + // initializes default values by caching + this.isEnabled = this.config.getBoolean( "enabled", "general", true, "If true, the version checker is enabled. Acts as a master switch." ); + + this.lastCheck = this.config.getString( "lastCheck", "cache", "0", "The number of milliseconds since January 1, 1970, 00:00:00 GMT of the last successful check." ); + this.interval = this.config.getInt( "interval", "cache", DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, MAX_INTERVAL_HOURS, "Waits as many hours, until it checks again." ); + + this.level = this.config.getString( "level", "channel", "Beta", "Determines the channel level which should be checked for updates. Can be either Release, Beta or Alpha." ); + + this.shouldNotifyPlayer = this.config.getBoolean( "notify", "client", true, "If true, the player is getting a notification, that a new version is available." ); + this.shouldPostChangelog = this.config.getBoolean( "changelog", "client", true, "If true, the player is getting a notification including changelog. Only happens if notification are enabled." ); + } + + public boolean isEnabled() + { + return this.isEnabled; + } + + public String lastCheck() + { + return this.lastCheck; + } + + /** + * Stores the current date in milli seconds into the "lastCheck" field of the config + * and makes it persistent. + */ + public void updateLastCheck() + { + final Date now = new Date(); + final long nowInMs = now.getTime(); + final String nowAsString = Long.toString( nowInMs ); + + this.config.get( "cache", "lastCheck", "0" ).set( nowAsString ); + + this.config.save(); + } + + public int interval() + { + return this.interval; + } + + public String level() + { + return this.level; + } + + public boolean shouldNotifyPlayer() + { + return this.shouldNotifyPlayer; + } + + public boolean shouldPostChangelog() + { + return this.shouldPostChangelog; + } +} diff --git a/src/main/java/appeng/services/version/VersionFetcher.java b/src/main/java/appeng/services/version/VersionFetcher.java new file mode 100644 index 00000000..d70fed83 --- /dev/null +++ b/src/main/java/appeng/services/version/VersionFetcher.java @@ -0,0 +1,10 @@ +package appeng.services.version; + + +/** + * Processes base information to retrieve a {@link Version} + */ +public interface VersionFetcher +{ + Version get(); +} diff --git a/src/main/java/appeng/services/version/VersionParser.java b/src/main/java/appeng/services/version/VersionParser.java new file mode 100644 index 00000000..14cad114 --- /dev/null +++ b/src/main/java/appeng/services/version/VersionParser.java @@ -0,0 +1,132 @@ +package appeng.services.version; + + +import java.security.InvalidParameterException; +import java.util.Scanner; +import java.util.regex.Pattern; + + +/** + * can parse a version in form of rv2-beta-8 or rv2.beta.8 + */ +public final class VersionParser +{ + private static final Pattern PATTERN_DOT = Pattern.compile( "\\." ); + private static final Pattern PATTERN_DASH = Pattern.compile( "-" ); + private static final Pattern PATTERN_REVISION = Pattern.compile( "[^0-9]+" ); + private static final Pattern PATTERN_BUILD = Pattern.compile( "[^0-9]+" ); + private static final Pattern PATTERN_NATURAL = Pattern.compile( "[0-9]+" ); + private static final Pattern PATTERN_VALID_REVISION = Pattern.compile( "^rv\\d+$" ); + + /** + * Parses the {@link Version} out of a String + * + * @param raw String in form of rv2-beta-8 or rv2.beta.8 + * + * @return {@link Version} encoded in the raw String + * + * @throws AssertionError if raw String does not match pattern of a {@link Version} + */ + public Version parse( String raw ) + { + final String transformed = this.transformDelimiter( raw ); + final String[] split = transformed.split( "_" ); + + return this.parseVersion( split ); + } + + /** + * Replaces all "." and "-" into "_" to make them uniform + * + * @param raw raw version string containing "." or "-" + * + * @return transformed raw, where "." and "-" are replaced by "_" + */ + private String transformDelimiter( String raw ) + { + assert raw.contains( "." ) || raw.contains( "-" ); + + final String withoutDot = PATTERN_DOT.matcher( raw ).replaceAll( "_" ); + final String withoutDash = PATTERN_DASH.matcher( withoutDot ).replaceAll( "_" ); + + return withoutDash; + } + + /** + * parses the {@link Version} out of the split. + * The split must have a length of 3, + * representing revision, channel and build. + * + * @param splitRaw raw version split with length of 3 + * + * @return {@link Version} represented by the splitRaw + */ + private Version parseVersion( String[] splitRaw ) + { + assert splitRaw.length == 3; + + final String rawRevision = splitRaw[0]; + final String rawChannel = splitRaw[1]; + final String rawBuild = splitRaw[2]; + + final int revision = this.parseRevision( rawRevision ); + final Channel channel = this.parseChannel( rawChannel ); + final int build = this.parseBuild( rawBuild ); + + return new DefaultVersion( revision, channel, build ); + } + + /** + * A revision starts with the keyword "rv", followed by a natural number + * + * @param rawRevision String containing the revision number + * + * @return revision number + */ + private int parseRevision( String rawRevision ) + { + assert PATTERN_VALID_REVISION.matcher( rawRevision ).matches(); + + final int revision = new Scanner( rawRevision ).useDelimiter( PATTERN_REVISION ).nextInt(); + + return revision; + } + + /** + * A channel is atm either one of {@link Channel#Alpha}, {@link Channel#Beta} or {@link Channel#Release} + * + * @param rawChannel String containing the channel + * + * @return matching {@link Channel} to the String + */ + private Channel parseChannel( String rawChannel ) + { + assert rawChannel.equalsIgnoreCase( Channel.Alpha.name() ) || rawChannel.equalsIgnoreCase( Channel.Beta.name() ) || rawChannel.equalsIgnoreCase( Channel.Release.name() ); + + for ( Channel channel : Channel.values() ) + { + if ( channel.name().equalsIgnoreCase( rawChannel ) ) + { + return channel; + } + } + + throw new InvalidParameterException( "Raw channel did not contain any of the pre-programmed types." ); + } + + /** + * A build is just a natural number + * + * @param rawBuild String containing the build number + * + * @return build number + */ + private int parseBuild( String rawBuild ) + { + assert PATTERN_NATURAL.matcher( rawBuild ).matches(); + + final int build = new Scanner( rawBuild ).useDelimiter( PATTERN_BUILD ).nextInt(); + + return build; + } +} diff --git a/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java b/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java new file mode 100644 index 00000000..1d63a0e4 --- /dev/null +++ b/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java @@ -0,0 +1,32 @@ +package appeng.services.version.github; + + +import appeng.services.version.Version; + + +/** + * Default template when a {@link FormattedRelease} is needed. + */ +public final class DefaultFormattedRelease implements FormattedRelease +{ + private final Version version; + private final String changelog; + + public DefaultFormattedRelease( Version version, String changelog ) + { + this.version = version; + this.changelog = changelog; + } + + @Override + public String changelog() + { + return this.changelog; + } + + @Override + public Version version() + { + return this.version; + } +} diff --git a/src/main/java/appeng/services/version/github/FormattedRelease.java b/src/main/java/appeng/services/version/github/FormattedRelease.java new file mode 100644 index 00000000..3bf1a333 --- /dev/null +++ b/src/main/java/appeng/services/version/github/FormattedRelease.java @@ -0,0 +1,21 @@ +package appeng.services.version.github; + + +import appeng.services.version.Version; + + +/** + * Represents the acquired, processed information through github about a release of Applied Energistics 2 + */ +public interface FormattedRelease +{ + /** + * @return changelog + */ + String changelog(); + + /** + * @return processed version + */ + Version version(); +} diff --git a/src/main/java/appeng/services/version/github/MissingFormattedRelease.java b/src/main/java/appeng/services/version/github/MissingFormattedRelease.java new file mode 100644 index 00000000..941bdac5 --- /dev/null +++ b/src/main/java/appeng/services/version/github/MissingFormattedRelease.java @@ -0,0 +1,37 @@ +package appeng.services.version.github; + + +import appeng.services.version.MissingVersion; +import appeng.services.version.Version; + + +/** + * Exceptional template, when no meaningful {@link FormattedRelease} could be obtained + */ +public final class MissingFormattedRelease implements FormattedRelease +{ + private final Version version; + + public MissingFormattedRelease() + { + this.version = new MissingVersion(); + } + + /** + * @return empty string + */ + @Override + public String changelog() + { + return ""; + } + + /** + * @return {@link MissingVersion} + */ + @Override + public Version version() + { + return this.version; + } +} diff --git a/src/main/java/appeng/services/version/github/Release.java b/src/main/java/appeng/services/version/github/Release.java new file mode 100644 index 00000000..105c5012 --- /dev/null +++ b/src/main/java/appeng/services/version/github/Release.java @@ -0,0 +1,19 @@ +package appeng.services.version.github; + + +/** + * Template class for Gson to write values from the Json Object into an actual class + */ +@SuppressWarnings( "ALL" ) +public class Release +{ + /** + * name of the tag it is saved + */ + public String tag_name; + + /** + * Contains the changelog + */ + public String body; +} diff --git a/src/main/java/appeng/services/version/github/ReleaseFetcher.java b/src/main/java/appeng/services/version/github/ReleaseFetcher.java new file mode 100644 index 00000000..69c70e7a --- /dev/null +++ b/src/main/java/appeng/services/version/github/ReleaseFetcher.java @@ -0,0 +1,90 @@ +package appeng.services.version.github; + + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URL; +import java.util.List; + +import org.apache.commons.io.IOUtils; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import appeng.core.AELog; +import appeng.services.version.Channel; +import appeng.services.version.Version; +import appeng.services.version.VersionCheckerConfig; +import appeng.services.version.VersionParser; + + +public final class ReleaseFetcher +{ + private static final String GITHUB_RELEASES_URL = "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases"; + private static final FormattedRelease EXCEPTIONAL_RELEASE = new MissingFormattedRelease(); + + private final VersionCheckerConfig config; + private final VersionParser parser; + + public ReleaseFetcher( VersionCheckerConfig config, VersionParser parser ) + { + this.config = config; + this.parser = parser; + } + + public FormattedRelease get() + { + final Gson gson = new Gson(); + final Type type = new ReleasesTypeToken().getType(); + + try + { + final URL releasesURL = new URL( GITHUB_RELEASES_URL ); + final String rawReleases = this.getRawReleases( releasesURL ); + + this.config.updateLastCheck(); + + final List releases = gson.fromJson( rawReleases, type ); + final FormattedRelease latestFitRelease = this.getLatestFitRelease( releases ); + + return latestFitRelease; + } + catch ( Exception e ) + { + AELog.error( e ); + + return EXCEPTIONAL_RELEASE; + } + } + + private String getRawReleases( URL url ) throws IOException + { + return IOUtils.toString( url ); + } + + private FormattedRelease getLatestFitRelease( Iterable releases ) + { + final String levelInConfig = this.config.level(); + final Channel level = Channel.valueOf( levelInConfig ); + final int levelOrdinal = level.ordinal(); + + for ( Release release : releases ) + { + final String rawVersion = release.tag_name; + final String changelog = release.body; + + final Version version = this.parser.parse( rawVersion ); + + if ( version.channel().ordinal() >= levelOrdinal ) + { + return new DefaultFormattedRelease( version, changelog ); + } + } + + return EXCEPTIONAL_RELEASE; + } + + private static final class ReleasesTypeToken extends TypeToken> + { + } +} diff --git a/src/test/java/appeng/services/version/VersionParserTest.java b/src/test/java/appeng/services/version/VersionParserTest.java new file mode 100644 index 00000000..854298e9 --- /dev/null +++ b/src/test/java/appeng/services/version/VersionParserTest.java @@ -0,0 +1,80 @@ +package appeng.services.version; + + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +/** + * Tests for {@link VersionParser} + */ +public final class VersionParserTest +{ + private static final String GITHUB_VERSION = "rv2.beta.8"; + private static final String GITHUB_INVALID_REVISION = "2.beta.8"; + private static final String GITHUB_INVALID_CHANNEL = "rv2.gamma.8"; + private static final String GITHUB_INVALID_BUILD = "rv2.beta.b8"; + private static final String MOD_VERSION = "rv2-beta-8"; + private static final String MOD_INVALID_REVISION = "2-beta-8"; + private static final String MOD_INVALID_CHANNEL = "rv2-gamma-8"; + private static final String MOD_INVALID_BUILD = "rv2-beta-b8"; + + private static final DefaultVersion VERSION = new DefaultVersion( 2, Channel.Beta, 8 ); + + private final VersionParser parser; + + public VersionParserTest() + { + this.parser = new VersionParser(); + } + + @Test + public void testParseGitHub_shouldPass() + { + assertTrue( this.parser.parse( GITHUB_VERSION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidRevision_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_REVISION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidChannel_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_CHANNEL ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidBuild_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_BUILD ).equals( VERSION ) ); + } + + @Test + public void testParseMod_shouldPass() + { + assertTrue( this.parser.parse( MOD_VERSION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidRevision_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_REVISION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidChannel_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_CHANNEL ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidBuild_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_BUILD ).equals( VERSION ) ); + } +} diff --git a/src/test/java/appeng/util/UUIDMatcherTest.java b/src/test/java/appeng/util/UUIDMatcherTest.java index 24a18bc6..54e52c18 100644 --- a/src/test/java/appeng/util/UUIDMatcherTest.java +++ b/src/test/java/appeng/util/UUIDMatcherTest.java @@ -28,7 +28,7 @@ import static org.junit.Assert.assertTrue; /** * Tests for {@link UUIDMatcher} */ -public class UUIDMatcherTest +public final class UUIDMatcherTest { private static final String IS_UUID = "03ba29a1-d6bd-32ba-90b2-375e4d65abc9"; private static final String NO_UUID = "no"; diff --git a/src/test/resources/appeng/services/version/releases.json b/src/test/resources/appeng/services/version/releases.json new file mode 100644 index 00000000..ee473838 --- /dev/null +++ b/src/test/resources/appeng/services/version/releases.json @@ -0,0 +1,1040 @@ +[ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.12", + "id": 1053651, + "tag_name": "rv2.beta.12", + "target_commitish": "4744dfab789b778c5c255a268ef4f1d4a01cca9c", + "name": "Build rv2.beta.12", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-15T17:38:03Z", + "published_at": "2015-03-16T06:32:39Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.12", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.12", + "body": "Fixes #1024 Added zinc to the grindstone - thatsIch\r\n\r\nUpdate zh_CN.lang - bakaxyf" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.11", + "id": 1051116, + "tag_name": "rv2.beta.11", + "target_commitish": "ece09a956bdd0fdf3cc0074ffeed5efbdf3a37a4", + "name": "Build rv2.beta.11", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-13T22:49:12Z", + "published_at": "2015-03-14T23:53:49Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.11", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.11", + "body": "Fixes #1015 Pattern terminal destroying a single item when not able to satisfy a NEI recipe - yueh\r\n\r\nFixes #1011 skip null values returned by the oredictionary - yueh\r\n\r\nUpdate zh_CN.lang - bakaxyf" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.10", + "id": 1038746, + "tag_name": "rv2.beta.10", + "target_commitish": "8ec9bb8b3b18a9fb4f350e326b267ff35c434217", + "name": "Build rv2.beta.10", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-11T15:40:32Z", + "published_at": "2015-03-11T15:53:37Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.10", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.10", + "body": "Fixes #861 Wireless Terminal notifies player, if it is unlinked - thatsIch\r\n\r\nFixes #920 Increased GUI close distance to match Vanilla MC distance - thatsIch\r\n\r\nFixes #743 Crash with BC plugs\r\n\r\nFixes #942 BC builder can build AE networks again\r\n\r\nUpdate #319 Now compiled against BC 6.4.2, with that MJ is gone" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.9", + "id": 1033060, + "tag_name": "rv2.beta.9", + "target_commitish": "3675f154d1f3af7cf34c52816f7014c74e7031b7", + "name": "Build rv2.beta.9", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-09T14:05:21Z", + "published_at": "2015-03-10T09:42:09Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.9", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.9", + "body": "Enhancement #817 Crafting CPU GUI now shows a green background on items being crafted, and a yellow background on scheduled items - Eearslya\r\n\r\nEnhancement Added an option to disable the colored crafting status - yueh\r\n\r\nEnhancement Added more information to the security audit - yueh\r\n\r\nEnhancement Improved performance - yueh\r\n\r\nFixes #972 Level Emitter tool-tip now corresponds to the actual behavior - thatsIch\r\n\r\nFixes #982 Auto-crafting lag reduced - yueh\r\n\r\nFixes #910 Dense cables render correctly with FMP now - yueh\r\n\r\nFixes #943 Reduced lag when breaking multiple blocks at once using Annihilation Plane - yueh\r\n\r\nFixes cable render crash and covered cables render as smart - yueh\r\n\r\nUpdate fr_FR.lang - Mazdallier\r\n\r\nUpdate zh_CN.lang - bakaxyf\r\n\r\nUpdated Texture / Model License to be more specific - AlgorithmX2\r\n\r\nUpdated Forge to recommended 10.13.2.1291 and other dependencies - yueh" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.8", + "id": 881276, + "tag_name": "rv2.beta.8", + "target_commitish": "ef3cb0a4559ababe53cd7f45123e7b28719d06b7", + "name": "Build rv2.beta.8", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-01-22T16:05:23Z", + "published_at": "2015-01-24T16:46:55Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.8", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.8", + "body": "Update pt_BR.lang - al-myr\r\n\r\nImproved the logged warning for FailledConnection Now displays to coordinates of the affected tunnels. - yueh\r\n\r\nFix #701 - Changed harvest tool to axe - TheJulianJES\r\n\r\nFormation plane can now drop blocks as items - yueh\r\n\r\nDisable level emitter on offline networks Turns off level emitters if the goes offline because it runs out of power, channels or similar. - yueh\r\n\r\nFixes #304 WAILA Integration updated and fixed WAILA offers a new interface to sync the server data to the client. - thatsIch\r\n\r\nRemoved buffer from annihilation plane - yueh\r\n\r\nEffects should not collide with anything - yueh\r\n\r\nImprove code quality - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.7", + "id": 818857, + "tag_name": "rv2.beta.7", + "target_commitish": "15ba1970837804dc34de3cdae037fca511103f15", + "name": "Build rv2.beta.7", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-01-01T14:35:57Z", + "published_at": "2015-01-01T17:56:50Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.7", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.7", + "body": "Fixes #666 vanishing microblocks with Immibis-Microblocks - yueh" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.6", + "id": 816514, + "tag_name": "rv2.beta.6", + "target_commitish": "305fc6c7f2c85afa05e37e127098fc3e0330a9c2", + "name": "Build rv2.beta.6", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-30T22:34:57Z", + "published_at": "2014-12-30T22:40:56Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361179", + "id": 361179, + "name": "appliedenergistics2-rv2-beta-6-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 2, + "created_at": "2014-12-30T22:42:44Z", + "updated_at": "2014-12-30T22:42:44Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361180", + "id": 361180, + "name": "appliedenergistics2-rv2-beta-6-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2388010, + "download_count": 1, + "created_at": "2014-12-30T22:42:47Z", + "updated_at": "2014-12-30T22:42:48Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361178", + "id": 361178, + "name": "appliedenergistics2-rv2-beta-6.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424995, + "download_count": 10, + "created_at": "2014-12-30T22:42:37Z", + "updated_at": "2014-12-30T22:42:39Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.6", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.6", + "body": "Fixes #663 NPE on disabled Facades - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.5", + "id": 815358, + "tag_name": "rv2.beta.5", + "target_commitish": "97dabc4a5cb2117f872946aea359dc49efa3b79b", + "name": "Build rv2.beta.5", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-30T13:06:08Z", + "published_at": "2014-12-30T13:31:11Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360438", + "id": 360438, + "name": "appliedenergistics2-rv2-beta-5-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 0, + "created_at": "2014-12-30T15:03:30Z", + "updated_at": "2014-12-30T15:03:30Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360439", + "id": 360439, + "name": "appliedenergistics2-rv2-beta-5-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2387747, + "download_count": 0, + "created_at": "2014-12-30T15:03:32Z", + "updated_at": "2014-12-30T15:03:33Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360437", + "id": 360437, + "name": "appliedenergistics2-rv2-beta-5.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424731, + "download_count": 3, + "created_at": "2014-12-30T15:03:25Z", + "updated_at": "2014-12-30T15:03:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.5", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.5", + "body": "Fixes #659 NPE on usage of the FZ sculpting tool - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.4", + "id": 814288, + "tag_name": "rv2.beta.4", + "target_commitish": "02e59faa59ed1715366d0a659197dda9808560df", + "name": "Build rv2.beta.4", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-29T22:02:48Z", + "published_at": "2014-12-29T22:44:26Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360441", + "id": 360441, + "name": "appliedenergistics2-rv2-beta-4-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 0, + "created_at": "2014-12-30T15:03:48Z", + "updated_at": "2014-12-30T15:03:48Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360442", + "id": 360442, + "name": "appliedenergistics2-rv2-beta-4-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2387673, + "download_count": 0, + "created_at": "2014-12-30T15:03:50Z", + "updated_at": "2014-12-30T15:03:52Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360440", + "id": 360440, + "name": "appliedenergistics2-rv2-beta-4.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424666, + "download_count": 0, + "created_at": "2014-12-30T15:03:45Z", + "updated_at": "2014-12-30T15:03:46Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.4", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.4", + "body": "Fixes #644 Several missing null checks when disabling huge chunks of the mod via config - thatsIch\r\n\r\nUpdate ru_RU.lang - Adaptivity" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.3", + "id": 812048, + "tag_name": "rv2.beta.3", + "target_commitish": "e712a9d8ceca11fd9571625a1902eab2173909b4", + "name": "Build rv2.beta.3", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-28T17:38:56Z", + "published_at": "2014-12-28T17:46:49Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360444", + "id": 360444, + "name": "appliedenergistics2-rv2-beta-3-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264396, + "download_count": 1, + "created_at": "2014-12-30T15:04:28Z", + "updated_at": "2014-12-30T15:04:29Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360445", + "id": 360445, + "name": "appliedenergistics2-rv2-beta-3-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2386391, + "download_count": 0, + "created_at": "2014-12-30T15:04:30Z", + "updated_at": "2014-12-30T15:04:32Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360443", + "id": 360443, + "name": "appliedenergistics2-rv2-beta-3.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2423384, + "download_count": 0, + "created_at": "2014-12-30T15:04:25Z", + "updated_at": "2014-12-30T15:04:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.3", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.3", + "body": "Fixes #639 Missing null check for people with disabled channels - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.2", + "id": 811619, + "tag_name": "rv2.beta.2", + "target_commitish": "f7323289a50f75b6b68f2fe06bf2a6bd593d70e3", + "name": "Build rv2.beta.2", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-27T18:05:37Z", + "published_at": "2014-12-28T08:49:55Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360447", + "id": 360447, + "name": "appliedenergistics2-rv2-beta-2-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264396, + "download_count": 0, + "created_at": "2014-12-30T15:04:52Z", + "updated_at": "2014-12-30T15:04:53Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360448", + "id": 360448, + "name": "appliedenergistics2-rv2-beta-2-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2386310, + "download_count": 0, + "created_at": "2014-12-30T15:04:55Z", + "updated_at": "2014-12-30T15:04:57Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360446", + "id": 360446, + "name": "appliedenergistics2-rv2-beta-2.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2423307, + "download_count": 2, + "created_at": "2014-12-30T15:04:47Z", + "updated_at": "2014-12-30T15:04:49Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.2", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.2", + "body": "* #624 Fixes disabled features getting not disabled - thatsIch\r\n* #572 Fixes inventory validation for any kind of Player (enables automation via robots e.g.) - thatsIch\r\n* Sound improvements - TheJulianJES\r\n* Use player inventory to let NEI fill a recipe when it is not provided by the network - riking\r\n* #604 Fixes crash on Achievement - thatsIch\r\n* #593 Updating RF Integration to use version 1.7.10R1.0.2 - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.1", + "id": 783731, + "tag_name": "rv2.beta.1", + "target_commitish": "8d1a0cdb7df49646d4e7a713eed30c546fff2f4a", + "name": "Build rv2.beta.1", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-15T13:59:55Z", + "published_at": "2014-12-15T14:07:25Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360452", + "id": 360452, + "name": "appliedenergistics2-rv2-beta-1-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264402, + "download_count": 0, + "created_at": "2014-12-30T15:05:28Z", + "updated_at": "2014-12-30T15:05:28Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360453", + "id": 360453, + "name": "appliedenergistics2-rv2-beta-1-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2383463, + "download_count": 0, + "created_at": "2014-12-30T15:05:30Z", + "updated_at": "2014-12-30T15:05:32Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360451", + "id": 360451, + "name": "appliedenergistics2-rv2-beta-1.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2420383, + "download_count": 2, + "created_at": "2014-12-30T15:05:25Z", + "updated_at": "2014-12-30T15:05:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.1", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.1", + "body": "Bump channel to beta" + } +]