Added ModStat integration

This commit is contained in:
Calclavia 2013-08-06 17:58:17 -04:00
parent 05935a5805
commit 4324901ee4
10 changed files with 891 additions and 0 deletions

View file

@ -20,6 +20,10 @@
<exclude name=".git/**"/>
<exclude name="**/*.xml"/>
</fileset>
<fileset dir="${dir.development}Universal-Electricity/src">
<exclude name=".git/**"/>
<exclude name="**/*.xml"/>
</fileset>
</copy>
<mkdir dir="output" />
@ -51,6 +55,11 @@
<exclude name="**/*.java"/>
<exclude name="**/*.xml"/>
</fileset>
<fileset dir="${dir.development}Universal-Electricity/src">
<exclude name=".git/**"/>
<exclude name="**/*.xml"/>
</fileset>
</copy>
<copy todir="APIContents">

View file

@ -0,0 +1,35 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats;
public interface IModstatsReporter
{
public void registerMod(Object mod);
public void doManualCheck();
}

View file

@ -0,0 +1,117 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats;
import java.util.HashMap;
import java.util.Map;
public class ModVersionData
{
public String prefix;
public String name;
public String version;
public String downloadUrl;
public String changeLogUrl;
public Map<String, String> extraFields;
public ModVersionData()
{
extraFields = new HashMap<String, String>();
}
public ModVersionData(String prefix, String name, String version)
{
this.prefix = prefix;
this.name = name;
this.version = version;
extraFields = new HashMap<String, String>();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((changeLogUrl == null) ? 0 : changeLogUrl.hashCode());
result = prime * result + ((downloadUrl == null) ? 0 : downloadUrl.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ModVersionData other = (ModVersionData) obj;
if (changeLogUrl == null)
{
if (other.changeLogUrl != null)
return false;
}
else if (!changeLogUrl.equals(other.changeLogUrl))
return false;
if (downloadUrl == null)
{
if (other.downloadUrl != null)
return false;
}
else if (!downloadUrl.equals(other.downloadUrl))
return false;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
if (prefix == null)
{
if (other.prefix != null)
return false;
}
else if (!prefix.equals(other.prefix))
return false;
if (version == null)
{
if (other.version != null)
return false;
}
else if (!version.equals(other.version))
return false;
return true;
}
}

View file

@ -0,0 +1,64 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats;
import java.util.LinkedList;
import java.util.List;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
import cpw.mods.fml.common.FMLLog;
@Cancelable
public class ModsUpdateEvent extends Event
{
private List<ModVersionData> updatedMods;
public ModsUpdateEvent()
{
updatedMods = new LinkedList<ModVersionData>();
}
public void add(ModVersionData data)
{
if (!updatedMods.contains(data))
{
updatedMods.add(data);
}
else
{
FMLLog.info("ModsUpdateEvent shouldn't have same mods data", data);
}
}
public List<ModVersionData> getUpdatedMods()
{
return updatedMods;
}
}

View file

@ -0,0 +1,59 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModstatInfo
{
/**
* Modstats mod prefix.
*
* @return
*/
public String prefix();
/**
* Mod name. Use this if your mod doesn't have @Mod annotation
*
* @return
*/
public String name() default "";
/**
* Mod version. Use this if your mod doesn't have @Mod annotation
*
* @return
*/
public String version() default "";
}

View file

@ -0,0 +1,91 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats;
import cpw.mods.fml.common.FMLLog;
public class Modstats
{
private static final Modstats INSTANCE = new Modstats();
private static final String CLASS_TEMPLATE = "org.modstats.reporter.v%d.Reporter";
private IModstatsReporter reporter;
private Modstats()
{
reporter = locateReporter();
}
public IModstatsReporter getReporter()
{
return reporter;
}
private IModstatsReporter locateReporter()
{
int i = 1;
Class<?> latest = null;
while (i < 100)
{
try
{
Class<?> candidate = Class.forName(String.format(CLASS_TEMPLATE, i));
if (IModstatsReporter.class.isAssignableFrom(candidate))
{
latest = candidate;
}
}
catch (Exception e)
{
break;
}
i++;
}
if (latest == null)
{
FMLLog.warning("Modstats reporter class not found.");
}
else
{
try
{
return (IModstatsReporter) latest.newInstance();
}
catch (Exception e)
{
FMLLog.warning("Modstats reporter class can't be instantiated.");
}
}
return null;
}
public static Modstats instance()
{
return INSTANCE;
}
}

View file

@ -0,0 +1,73 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats.reporter.v1;
import java.io.File;
import net.minecraftforge.common.Configuration;
import net.minecraftforge.common.Property;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
public class Config
{
private static final String CONFIG_NAME = "modstats.cfg";
public boolean allowUpdates;
public boolean betaNotifications;
public boolean forCurrentMinecraftVersion;
public boolean logOnly;
public Config()
{
File configLocation = new File(Loader.instance().getConfigDir(), CONFIG_NAME);
Configuration configuration = new Configuration(configLocation);
configuration.load();
Property prop = configuration.get("updates", "AllowUpdates", true);
prop.comment = "Allow to send current mod versions to the server and check for updates.\nIt allows to mod authors to see mod's popularity. Please don't disable it without necessity";
allowUpdates = prop.getBoolean(true);
prop = configuration.get("updates", "LogOnly", false);
prop.comment = "Don't display chat message, just add message to the log.";
logOnly = prop.getBoolean(false);
prop = configuration.get("updates", "BetaNotifications", false);
prop.comment = "Set true to receive notifications about beta versions. Otherwise you will only receive information about stable versions";
betaNotifications = prop.getBoolean(false);
prop = configuration.get("updates", "ForCurrentMinecraftVersion", false);
prop.comment = "Check for updates only for current MC version.\nEx:if you have MC 1.4.2 and ForCurrentMinecraftVersion is true, then you wouldn't receive notifications about versions for MC 1.4.5";
forCurrentMinecraftVersion = prop.getBoolean(false);
configuration.save();
FMLLog.info("[Modstats] Config loaded. allowUpdates: %b, betaNotification: %b, strict: %b", allowUpdates, betaNotifications, forCurrentMinecraftVersion);
}
}

View file

@ -0,0 +1,296 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats.reporter.v1;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.crash.CallableMinecraftVersion;
import net.minecraftforge.common.MinecraftForge;
import org.modstats.ModVersionData;
import org.modstats.ModsUpdateEvent;
import argo.jdom.JdomParser;
import argo.jdom.JsonNode;
import argo.jdom.JsonRootNode;
import argo.jdom.JsonStringNode;
import argo.saj.InvalidSyntaxException;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.versioning.ComparableVersion;
class DataSender extends Thread
{
private static final String urlAutoTemplate = "http://modstats.org/api/v1/report?mc=%s&user=%s&data=%s&sign=%s&beta=%b&strict=%b";
private static final String urlManualTemplate = "http://modstats.org/api/v1/check?mc=%s&user=%s&data=%s&sign=%s&beta=%b&strict=%b";
private final Reporter reporter;
public final boolean manual;
public DataSender(Reporter reporter, boolean manual)
{
this.reporter = reporter;
this.manual = manual;
}
private String toHexString(byte[] bytes)
{
char[] hexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++)
{
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v / 16];
hexChars[j * 2 + 1] = hexArray[v % 16];
}
return new String(hexChars);
}
private String getPlayerId() throws IOException
{
File statDir = new File(FMLClientHandler.instance().getClient().mcDataDir, "stats");
if (!statDir.exists())
{
statDir.mkdirs();
}
String mac = "";
try
{
InetAddress address = InetAddress.getLocalHost();
NetworkInterface ni = NetworkInterface.getByInetAddress(address);
byte[] macArray = ni.getHardwareAddress();
if (macArray != null)
{
mac = toHexString(macArray);
}
}
catch (Exception ex)
{
}
File uidFile = new File(statDir, "player.uid");
if (uidFile.exists() && uidFile.canRead() && uidFile.length() == 32 + mac.length())
{
String data = Files.toString(uidFile, Charsets.US_ASCII);
String storedMac = data.substring(32);
if (storedMac.equalsIgnoreCase(mac))
return data.substring(0, 32);
}
uidFile.createNewFile();
if (uidFile.canWrite())
{
String uid = UUID.randomUUID().toString().replace("-", "");
FileOutputStream output = new FileOutputStream(uidFile);
output.write((uid + mac).getBytes());
output.close();
return uid;
}
return "";
}
private String getSignature(String data)
{
return Hashing.md5().hashString(data).toString();
}
private String getData()
{
StringBuilder b = new StringBuilder();
for (Map.Entry<String, ModVersionData> item : reporter.registeredMods.entrySet())
{
b.append(item.getKey()).append("+").append(item.getValue().version).append("$");
}
return b.toString();
}
private boolean checkIsNewer(String current, String received)
{
return new ComparableVersion(received).compareTo(new ComparableVersion(current)) > 0;
}
private void parseResponse(String response)
{
try
{
JsonRootNode json = (new JdomParser()).parse(response);
// empty result
if (!json.isNode("mods"))
{
FMLLog.info("[Modstats] Empty result");
return;
}
List<JsonNode> modList = json.getArrayNode("mods");
ModsUpdateEvent event = new ModsUpdateEvent();
for (JsonNode modObject : modList)
{
String prefix = modObject.getStringValue("code");
if (!reporter.registeredMods.containsKey(prefix))
{
FMLLog.warning("[Modstats] Extra mod '%s' in service response", prefix);
continue;
}
String version = modObject.getStringValue("ver");
if (version == null || version.equals(reporter.registeredMods.get(prefix).version))
{
continue;
}
if (checkIsNewer(reporter.registeredMods.get(prefix).version, version))
{
ModVersionData data = new ModVersionData(prefix, reporter.registeredMods.get(prefix).name, version);
Map<JsonStringNode, JsonNode> fields = modObject.getFields();
for (Map.Entry<JsonStringNode, JsonNode> entry : fields.entrySet())
{
String fieldName = entry.getKey().getText();
if (fieldName.equals("code") || fieldName.equals("ver"))
continue;
if (!(entry.getValue() instanceof JsonStringNode))
{
FMLLog.warning(String.format("[Modstats] Too complex data in response for field '%s'.", fieldName));
continue;
}
String value = ((JsonStringNode) entry.getValue()).getText();
if (fieldName.equals("chlog"))
{
data.changeLogUrl = value;
}
else if (fieldName.equals("link"))
{
data.downloadUrl = value;
}
else
{
data.extraFields.put(fieldName, value);
}
}
event.add(data);
}
}
if (event.getUpdatedMods().size() > 0)
{
MinecraftForge.EVENT_BUS.post(event);
}
if (!event.isCanceled() && event.getUpdatedMods().size() > 0)
{
List<ModVersionData> updatedModsToOutput = event.getUpdatedMods();
StringBuilder builder = new StringBuilder("Updates found: ");
Iterator<ModVersionData> iterator = updatedModsToOutput.iterator();
while (iterator.hasNext())
{
ModVersionData modVersionData = iterator.next();
builder.append(modVersionData.name).append(" (").append(modVersionData.version).append(")").append(iterator.hasNext() ? "," : ".");
}
FMLLog.info("[Modstats] %s", builder.toString());
if (!reporter.config.logOnly && FMLCommonHandler.instance().getSide().isClient())
{
Minecraft mc = FMLClientHandler.instance().getClient();
int maxTries = 30;
while (mc.thePlayer == null && maxTries > 0)
{
try
{
sleep(1000);
}
catch (InterruptedException e)
{
}
maxTries--;
}
if (mc.thePlayer != null)
{
mc.thePlayer.addChatMessage(builder.toString());
}
}
}
}
catch (InvalidSyntaxException e)
{
FMLLog.warning("[Modstats] Can't parse response: '%s'.", e.getMessage());
}
}
@Override
public void run()
{
try
{
String data = getData();
String playerId = getPlayerId();
String hash = getSignature(playerId + "!" + data);
String template = manual ? urlManualTemplate : urlAutoTemplate;
String mcVersion = new CallableMinecraftVersion(null).minecraftVersion();
URL url = new URL(String.format(template, mcVersion, playerId, data, hash, reporter.config.betaNotifications, reporter.config.forCurrentMinecraftVersion));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
String out = "";
while ((line = reader.readLine()) != null)
{
// in most cases it will contain just one line
out += line;
}
reader.close();
parseResponse(out);
}
catch (MalformedURLException e)
{
FMLLog.warning("[Modstats] Invalid stat report url");
}
catch (IOException e)
{
FMLLog.info("[Modstats] Stat wasn't reported '" + e.getMessage() + "'");
}
catch (Exception e)
{
FMLLog.warning("[Modstats] Something wrong: " + e.toString());
}
}
}

View file

@ -0,0 +1,142 @@
/**
* Copyright (c) <2012>, Oleg Romanovskiy <shedarhome@gmail.com> aka Shedar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.modstats.reporter.v1;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.ForgeSubscribe;
import net.minecraftforge.event.world.WorldEvent;
import org.modstats.IModstatsReporter;
import org.modstats.ModVersionData;
import org.modstats.ModstatInfo;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Mod;
public class Reporter implements IModstatsReporter
{
public Map<String, ModVersionData> registeredMods;
private DataSender sender;
public Config config;
/**
* At least one auto check was completed successfully
*/
private boolean checkedAuto;
public Reporter()
{
checkedAuto = false;
registeredMods = new ConcurrentHashMap<String, ModVersionData>(2, 0.9f, 1);
MinecraftForge.EVENT_BUS.register(this);
config = new Config();
}
private void startCheck(boolean manual)
{
if (!config.allowUpdates)
return;
// only manual check is allowed on servers
if (!FMLCommonHandler.instance().getSide().isClient() && !manual)
return;
if (registeredMods.isEmpty())
return;
DataSender currentSender = sender;
if (!manual && checkedAuto)
return;
if (currentSender != null && (currentSender.manual == false || manual))
return;
currentSender = new DataSender(this, manual);
currentSender.start();
sender = currentSender;
}
@ForgeSubscribe
public void worldLoad(WorldEvent.Load event)
{
startCheck(false);
}
@Override
public void registerMod(Object mod)
{
if (!config.allowUpdates)
return;
if (mod == null)
{
FMLLog.warning("[Modstats] Can't register null mod.");
return;
}
ModstatInfo info = mod.getClass().getAnnotation(ModstatInfo.class);
if (info == null)
{
FMLLog.warning("[Modstats] ModstatsInfo annotation not found for given mod.");
return;
}
if (info.prefix() == null || info.prefix().equals(""))
{
FMLLog.warning("[Modstats] Mod prefix can't be empty.");
return;
}
Mod modData = mod.getClass().getAnnotation(Mod.class);
ModVersionData data;
if (modData == null)
{
if (info.name() == null || info.name().equals(""))
{
FMLLog.warning("[Modstats] Mod name can't be empty.");
return;
}
if (info.version() == null || info.version().equals(""))
{
FMLLog.warning("[Modstats] Mod version can't be empty.");
return;
}
data = new ModVersionData(info.prefix(), info.name(), info.version());
}
else
{
data = new ModVersionData(info.prefix(), modData.name(), modData.version());
}
registeredMods.put(info.prefix(), data);
}
@Override
public void doManualCheck()
{
startCheck(true);
}
}

View file

@ -4,6 +4,9 @@ import java.io.File;
import java.util.Arrays;
import java.util.logging.Logger;
import org.modstats.ModstatInfo;
import org.modstats.Modstats;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@ -44,6 +47,7 @@ import cpw.mods.fml.common.registry.LanguageRegistry;
*/
@Mod(modid = ResonantInduction.ID, name = ResonantInduction.NAME, version = ResonantInduction.VERSION)
@NetworkMod(channels = ResonantInduction.CHANNEL, clientSideRequired = true, serverSideRequired = false, packetHandler = PacketHandler.class)
@ModstatInfo(prefix = "resonantin")
public class ResonantInduction
{
/**
@ -127,6 +131,7 @@ public class ResonantInduction
{
LOGGER.setParent(FMLLog.getLogger());
NetworkRegistry.instance().registerGuiHandler(this, ResonantInduction.proxy);
Modstats.instance().getReporter().registerMod(this);
CONFIGURATION.load();