mirror of
https://e.coding.net/circlecloud/YumCore.git
synced 2024-11-24 02:08:48 +00:00
feat: add local
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
parent
20cbb091ee
commit
67336e23f5
31
pom.xml
31
pom.xml
@ -7,37 +7,6 @@
|
|||||||
<version>1.9.7</version>
|
<version>1.9.7</version>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
|
||||||
<version>2.4</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>jar-no-fork</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
|
||||||
<version>2.10.3</version>
|
|
||||||
<configuration>
|
|
||||||
<failOnError>false</failOnError>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>attach-javadocs</id>
|
|
||||||
<goals>
|
|
||||||
<goal>jar</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
</build>
|
||||||
<ciManagement>
|
<ciManagement>
|
||||||
<system>Jenkins</system>
|
<system>Jenkins</system>
|
||||||
|
@ -23,19 +23,17 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
public class C {
|
public class C {
|
||||||
public static boolean init;
|
public static boolean init;
|
||||||
private static boolean above_1_16 = false;
|
|
||||||
private static Class<?> nmsIChatBaseComponent;
|
private static Class<?> nmsIChatBaseComponent;
|
||||||
private static Constructor<?> packetTypeConstructor;
|
private static Constructor<?> packetTypeConstructor;
|
||||||
private static Method chatSerializer;
|
private static Method chatSerializer;
|
||||||
private static Method getHandle;
|
private static Method getHandle;
|
||||||
private static Method nmsChatMessageTypeClassValueOf;
|
|
||||||
private static String version;
|
private static String version;
|
||||||
private static boolean newversion;
|
private static boolean newversion;
|
||||||
private static Field playerConnection;
|
private static Field playerConnection;
|
||||||
private static Method sendPacket;
|
private static Method sendPacket;
|
||||||
private static Object[] chatMessageTypes;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
try {
|
||||||
try {
|
try {
|
||||||
version = getNMSVersion();
|
version = getNMSVersion();
|
||||||
Integer subVersion = Integer.parseInt(version.split("_")[1]);
|
Integer subVersion = Integer.parseInt(version.split("_")[1]);
|
||||||
@ -56,23 +54,8 @@ public class C {
|
|||||||
}
|
}
|
||||||
if (c.getParameterTypes().length == 3) {
|
if (c.getParameterTypes().length == 3) {
|
||||||
packetTypeConstructor = c;
|
packetTypeConstructor = c;
|
||||||
above_1_16 = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Class<?> nmsChatMessageTypeClass = packetTypeConstructor.getParameterTypes()[1];
|
|
||||||
if (nmsChatMessageTypeClass.isEnum()) {
|
|
||||||
chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants();
|
|
||||||
} else {
|
|
||||||
switch (nmsChatMessageTypeClass.getName()) {
|
|
||||||
case "int":
|
|
||||||
nmsChatMessageTypeClass = Integer.class;
|
|
||||||
break;
|
|
||||||
case "byte":
|
|
||||||
nmsChatMessageTypeClass = Byte.class;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nmsChatMessageTypeClassValueOf = nmsChatMessageTypeClass.getDeclaredMethod("valueOf", String.class);
|
|
||||||
}
|
|
||||||
Class<?> typeCraftPlayer = Class.forName(b("entity.CraftPlayer"));
|
Class<?> typeCraftPlayer = Class.forName(b("entity.CraftPlayer"));
|
||||||
Class<?> typeNMSPlayer = subVersion < 17 ? Class.forName(a("EntityPlayer")) : Class.forName("net.minecraft.server.level.EntityPlayer");
|
Class<?> typeNMSPlayer = subVersion < 17 ? Class.forName(a("EntityPlayer")) : Class.forName("net.minecraft.server.level.EntityPlayer");
|
||||||
Class<?> typePlayerConnection = subVersion < 17 ? Class.forName(a("PlayerConnection")) : Class.forName("net.minecraft.server.network.PlayerConnection");
|
Class<?> typePlayerConnection = subVersion < 17 ? Class.forName(a("PlayerConnection")) : Class.forName("net.minecraft.server.network.PlayerConnection");
|
||||||
@ -85,6 +68,10 @@ public class C {
|
|||||||
} else {
|
} else {
|
||||||
sendPacket = typePlayerConnection.getMethod("a", Class.forName("net.minecraft.network.protocol.Packet"));
|
sendPacket = typePlayerConnection.getMethod("a", Class.forName("net.minecraft.network.protocol.Packet"));
|
||||||
}
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.d(ex);
|
||||||
|
Chat.getBukkitChatInvoke();
|
||||||
|
}
|
||||||
init = true;
|
init = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w("C 兼容性工具初始化失败 可能造成部分功能不可用!");
|
Log.w("C 兼容性工具初始化失败 可能造成部分功能不可用!");
|
||||||
@ -122,17 +109,7 @@ public class C {
|
|||||||
* 2. ActionBar
|
* 2. ActionBar
|
||||||
*/
|
*/
|
||||||
public static void sendJson(org.bukkit.entity.Player receivingPacket, String json, int type) {
|
public static void sendJson(org.bukkit.entity.Player receivingPacket, String json, int type) {
|
||||||
try {
|
Chat.getBukkitChatInvoke().send(receivingPacket, json, type);
|
||||||
Object serialized = chatSerializer.invoke(null, json);
|
|
||||||
Object player = getHandle.invoke(receivingPacket);
|
|
||||||
Object connection = playerConnection.get(player);
|
|
||||||
Object typeObj = chatMessageTypes == null ? nmsChatMessageTypeClassValueOf.invoke(null, String.valueOf(type)) : chatMessageTypes[type];
|
|
||||||
sendPacket.invoke(connection, above_1_16
|
|
||||||
? packetTypeConstructor.newInstance(serialized, typeObj, receivingPacket.getUniqueId())
|
|
||||||
: packetTypeConstructor.newInstance(serialized, typeObj));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Log.d("Json发包错误 " + version, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ActionBar {
|
public static class ActionBar {
|
||||||
|
272
src/main/java/pw/yumc/YumCore/bukkit/compatible/Chat.java
Normal file
272
src/main/java/pw/yumc/YumCore/bukkit/compatible/Chat.java
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
package pw.yumc.YumCore.bukkit.compatible;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class Chat {
|
||||||
|
private static final String version;
|
||||||
|
private static final BukkitChatInvoke bukkitChatInvoke;
|
||||||
|
|
||||||
|
static {
|
||||||
|
version = getNMSVersion();
|
||||||
|
int subVersion = Integer.parseInt(version.split("_")[1]);
|
||||||
|
if (subVersion >= 19) {
|
||||||
|
bukkitChatInvoke = new BukkitChatInvoke_1_19();
|
||||||
|
} else if (subVersion >= 17) {
|
||||||
|
bukkitChatInvoke = new BukkitChatInvoke_1_17_1();
|
||||||
|
} else if (subVersion >= 16) {
|
||||||
|
bukkitChatInvoke = new BukkitChatInvoke_1_16_5();
|
||||||
|
} else if (subVersion >= 8) {
|
||||||
|
bukkitChatInvoke = new BukkitChatInvoke_1_8();
|
||||||
|
} else {
|
||||||
|
bukkitChatInvoke = new BukkitChatInvoke_1_7_10();
|
||||||
|
}
|
||||||
|
bukkitChatInvoke.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BukkitChatInvoke getBukkitChatInvoke() {
|
||||||
|
return bukkitChatInvoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得NMS版本号
|
||||||
|
*
|
||||||
|
* @return NMS版本号
|
||||||
|
*/
|
||||||
|
public static String getNMSVersion() {
|
||||||
|
return Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> nmsCls(String str) throws ClassNotFoundException {
|
||||||
|
return Class.forName("net.minecraft.server." + version + "." + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> obcCls(String str) throws ClassNotFoundException {
|
||||||
|
return Class.forName("org.bukkit.craftbukkit." + version + "." + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class BukkitChatInvoke {
|
||||||
|
protected boolean downgrade = false;
|
||||||
|
|
||||||
|
protected Method chatSerializer;
|
||||||
|
protected Constructor<?> packetTypeConstructor;
|
||||||
|
protected Method getHandle;
|
||||||
|
protected Field playerConnection;
|
||||||
|
protected Method sendPacket;
|
||||||
|
protected Object[] chatMessageTypes;
|
||||||
|
|
||||||
|
protected Method componentSerializer;
|
||||||
|
|
||||||
|
BukkitChatInvoke() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
try {
|
||||||
|
Class<?> nmsChatSerializerClass = this.getNmsChatSerializerClass();
|
||||||
|
this.chatSerializer = this.getNmsChatSerializerMethod(nmsChatSerializerClass);
|
||||||
|
Constructor<?>[] constructors = this.getPacketPlayOutChatClass().getConstructors();
|
||||||
|
for (Constructor<?> constructor : constructors) {
|
||||||
|
if (constructor.getParameterCount() == 2 || constructor.getParameterCount() == 3) {
|
||||||
|
this.packetTypeConstructor = constructor;
|
||||||
|
if (this.isMatchPacketPlayOutChatClassConstructor(constructor)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getHandle = this.getGetHandleMethod();
|
||||||
|
this.playerConnection = this.getPlayerConnectionField(this.getHandle.getReturnType());
|
||||||
|
this.sendPacket = this.getSendPacketMethod(this.playerConnection.getType(), this.getPacketClass());
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
this.downgrade = true;
|
||||||
|
try {
|
||||||
|
this.componentSerializer = Class.forName("net.md_5.bungee.chat.ComponentSerializer").getMethod("parse", String.class);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
void json(Player receivingPacket, String json) {
|
||||||
|
if (this.downgrade) {
|
||||||
|
receivingPacket.spigot().sendMessage((BaseComponent[]) this.componentSerializer.invoke(null, json));
|
||||||
|
} else {
|
||||||
|
this.send(receivingPacket, json, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
void send(Player receivingPacket, String json, int type) {
|
||||||
|
this.sendPacket(receivingPacket, this.getPacketPlayOutChat(receivingPacket, json, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
void sendPacket(Player receivingPacket, Object packet) {
|
||||||
|
this.sendPacket.invoke(playerConnection.get(getHandle.invoke(receivingPacket)), packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract boolean isMatchPacketPlayOutChatClassConstructor(Constructor<?> constructor);
|
||||||
|
|
||||||
|
abstract Class<?> getNmsChatSerializerClass();
|
||||||
|
|
||||||
|
abstract Method getNmsChatSerializerMethod(Class<?> nmsChatSerializerClass);
|
||||||
|
|
||||||
|
abstract Class<?> getPacketPlayOutChatClass();
|
||||||
|
|
||||||
|
abstract Field getPlayerConnectionField(Class<?> nmsEntityPlayerClass);
|
||||||
|
|
||||||
|
abstract Method getGetHandleMethod();
|
||||||
|
|
||||||
|
abstract Class<?> getPacketClass();
|
||||||
|
|
||||||
|
abstract Method getSendPacketMethod(Class<?> playerConnectionClass, Class<?> packetClass);
|
||||||
|
|
||||||
|
abstract Object getPacketPlayOutChat(Player player, String json, int type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvokeBase extends BukkitChatInvoke {
|
||||||
|
@Override
|
||||||
|
protected boolean isMatchPacketPlayOutChatClassConstructor(Constructor<?> constructor) {
|
||||||
|
Class<?>[] types = constructor.getParameterTypes();
|
||||||
|
if (types[1].isEnum()) {
|
||||||
|
this.chatMessageTypes = types[1].getEnumConstants();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return types[1].getName().equals("int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getNmsChatSerializerClass() {
|
||||||
|
return nmsCls("ChatSerializer");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Method getNmsChatSerializerMethod(Class<?> nmsChatSerializerClass) {
|
||||||
|
return nmsChatSerializerClass.getMethod("a", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getPacketPlayOutChatClass() {
|
||||||
|
return nmsCls("PacketPlayOutChat");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Field getPlayerConnectionField(Class<?> nmsEntityPlayerClass) {
|
||||||
|
return nmsEntityPlayerClass.getField("playerConnection");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Method getGetHandleMethod() {
|
||||||
|
return obcCls("entity.CraftPlayer").getMethod("getHandle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getPacketClass() {
|
||||||
|
return nmsCls("Packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Method getSendPacketMethod(Class<?> playerConnectionClass, Class<?> packetClass) {
|
||||||
|
return playerConnectionClass.getMethod("sendPacket", packetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Object getPacketPlayOutChat(Player player, String json, int type) {
|
||||||
|
return packetTypeConstructor.newInstance(this.chatSerializer.invoke(null, json), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvoke_1_7_10 extends BukkitChatInvokeBase {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvoke_1_8 extends BukkitChatInvoke_1_7_10 {
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Object getPacketPlayOutChat(Player player, String json, int type) {
|
||||||
|
return packetTypeConstructor.newInstance(this.chatSerializer.invoke(null, json), this.chatMessageTypes[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getNmsChatSerializerClass() {
|
||||||
|
return nmsCls("IChatBaseComponent$ChatSerializer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvoke_1_16_5 extends BukkitChatInvoke_1_8 {
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Object getPacketPlayOutChat(Player player, String json, int type) {
|
||||||
|
return packetTypeConstructor.newInstance(this.chatSerializer.invoke(null, json), this.chatMessageTypes[type], player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 {
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getPacketPlayOutChatClass() {
|
||||||
|
return Class.forName("net.minecraft.network.protocol.game.PacketPlayOutChat");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getNmsChatSerializerClass() {
|
||||||
|
return Class.forName("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Field getPlayerConnectionField(Class<?> nmsEntityPlayerClass) {
|
||||||
|
return nmsEntityPlayerClass.getField("b");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getPacketClass() {
|
||||||
|
return Class.forName("net.minecraft.network.protocol.Packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitChatInvoke_1_19 extends BukkitChatInvoke_1_17_1 {
|
||||||
|
@Override
|
||||||
|
protected boolean isMatchPacketPlayOutChatClassConstructor(Constructor<?> constructor) {
|
||||||
|
Class<?>[] types = constructor.getParameterTypes();
|
||||||
|
return types[0] == String.class && types[1] == int.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Method getSendPacketMethod(Class<?> playerConnectionClass, Class<?> packetClass) {
|
||||||
|
return playerConnectionClass.getMethod("a", packetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Class<?> getPacketPlayOutChatClass() {
|
||||||
|
return Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
Object getPacketPlayOutChat(Player player, String json, int type) {
|
||||||
|
Object component = this.chatSerializer.invoke(null, json);
|
||||||
|
return this.packetTypeConstructor.newInstance(component, type == 0 ? 1 : type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -108,7 +108,7 @@ public class SubscribeTask implements Runnable, Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onJoin(PlayerJoinEvent e) {
|
public void onJoin(PlayerJoinEvent e) {
|
||||||
final Player player = e.getPlayer();
|
final Player player = e.getPlayer();
|
||||||
if (player.isOp() && updateFile.isUpdated()) {
|
if (player.isOp() && versionInfo.hasNewVersion() && updateFile.isUpdated()) {
|
||||||
Bukkit.getScheduler().runTaskLater(instance, () -> versionInfo.notify(player), 10);
|
Bukkit.getScheduler().runTaskLater(instance, () -> versionInfo.notify(player), 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,10 +255,13 @@ public class SubscribeTask implements Runnable, Listener {
|
|||||||
*/
|
*/
|
||||||
private Document document;
|
private Document document;
|
||||||
|
|
||||||
|
private boolean hasNewVersion;
|
||||||
|
|
||||||
public VersionInfo(Plugin plugin, String branch, boolean isSecret) {
|
public VersionInfo(Plugin plugin, String branch, boolean isSecret) {
|
||||||
this.name = plugin.getName();
|
this.name = plugin.getName();
|
||||||
this.version = plugin.getDescription().getVersion().split("-")[0];
|
this.version = plugin.getDescription().getVersion().split("-")[0];
|
||||||
this.info = String.format(isSecret ? pom : url, name, branch);
|
this.info = String.format(isSecret ? pom : url, name, branch);
|
||||||
|
this.hasNewVersion = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -372,7 +375,10 @@ public class SubscribeTask implements Runnable, Listener {
|
|||||||
Log.console("§4注意: §c当前版本为开发版本 且未开启全局调试 已自动下载最新稳定版!");
|
Log.console("§4注意: §c当前版本为开发版本 且未开启全局调试 已自动下载最新稳定版!");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (needUpdate(result, version)) {return result;}
|
if (needUpdate(result, version)) {
|
||||||
|
this.hasNewVersion = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d(e);
|
Log.d(e);
|
||||||
}
|
}
|
||||||
@ -382,5 +388,9 @@ public class SubscribeTask implements Runnable, Listener {
|
|||||||
public void update() throws ParserConfigurationException, IOException, SAXException {
|
public void update() throws ParserConfigurationException, IOException, SAXException {
|
||||||
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(info);
|
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasNewVersion() {
|
||||||
|
return hasNewVersion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user