Item Generator and Server finder

This commit is contained in:
ThebestkillerTBK
2022-10-10 22:33:05 +08:00
committed by Cloudburst
parent ea106d6f70
commit 97d42528bc
19 changed files with 1619 additions and 9 deletions

View File

@@ -0,0 +1,92 @@
package anticope.rejects.utils.server;
public class IPAddress {
private final int[] octets;
private final int port;
public IPAddress(int o1, int o2, int o3, int o4, int port) {
this.octets = new int[]{o1, o2, o3, o4};
this.port = port;
}
public IPAddress(int[] octets, int port) {
this.octets = octets;
this.port = port;
}
public static IPAddress fromText(String ip) {
String[] sections = ip.split(":");
if (sections.length < 1 || sections.length > 2)
return null;
int port = 25565;
if (sections.length == 2) {
try {
port = Integer.parseInt(sections[1].trim());
} catch (NumberFormatException e) {
return null;
}
}
int[] octets = new int[4];
String[] address = sections[0].trim().split("\\.");
if (address.length != 4)
return null;
for (int i = 0; i < 4; i++) {
try {
octets[i] = Integer.parseInt(address[i].trim());
} catch (NumberFormatException e) {
return null;
}
}
return new IPAddress(octets, port);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof IPAddress other))
return false;
if (octets.length != other.octets.length)
return false;
if (port != other.port)
return false;
for (int i = 0; i < octets.length; i++)
if (octets[i] != other.octets[i])
return false;
return true;
}
@Override
public int hashCode() {
assert (octets.length == 4);
int hash = 43;
hash = hash * 59 + octets[0];
hash = hash * 83 + octets[1];
hash = hash * 71 + octets[2];
hash = hash * 17 + octets[3];
hash = hash * 31 + port;
return hash;
}
@Override
public String toString() {
if (octets.length == 0)
return null;
StringBuilder result = new StringBuilder();
result.append(octets[0]);
for (int i = 1; i < octets.length; i++) {
result.append('.').append(octets[i]);
}
result.append(':').append(port);
return result.toString();
}
}

View File

@@ -0,0 +1,7 @@
package anticope.rejects.utils.server;
public interface IServerFinderDisconnectListener {
void onServerDisconnect();
void onServerFailed();
}

View File

@@ -0,0 +1,7 @@
package anticope.rejects.utils.server;
public interface IServerFinderDoneListener {
void onServerDone(ServerPinger pinger);
void onServerFailed(ServerPinger pinger);
}

View File

@@ -0,0 +1,60 @@
package anticope.rejects.utils.server;
import anticope.rejects.MeteorRejectsAddon;
import net.minecraft.client.network.MultiplayerServerListPinger;
import net.minecraft.client.network.ServerInfo;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicInteger;
public class LegacyServerPinger {
private static final AtomicInteger threadNumber = new AtomicInteger(0);
private ServerInfo server;
private boolean done = false;
private boolean failed = false;
public void ping(String ip) {
ping(ip, 25565);
}
public void ping(String ip, int port) {
server = new ServerInfo("", ip + ":" + port, false);
new Thread(() -> pingInCurrentThread(ip, port),
"Server Pinger #" + threadNumber.incrementAndGet()).start();
}
private void pingInCurrentThread(String ip, int port) {
MultiplayerServerListPinger pinger = new MultiplayerServerListPinger();
MeteorRejectsAddon.LOG.info("Pinging " + ip + ":" + port + "...");
try {
pinger.add(server, () -> {
});
MeteorRejectsAddon.LOG.info("Ping successful: " + ip + ":" + port);
} catch (UnknownHostException e) {
MeteorRejectsAddon.LOG.warn("Unknown host: " + ip + ":" + port);
failed = true;
} catch (Exception e2) {
MeteorRejectsAddon.LOG.warn("Ping failed: " + ip + ":" + port);
failed = true;
}
pinger.cancel();
done = true;
}
public boolean isStillPinging() {
return !done;
}
public boolean isWorking() {
return !failed;
}
public String getServerIP() {
return server.address;
}
}

View File

@@ -0,0 +1,37 @@
package anticope.rejects.utils.server;
import net.minecraft.SharedConstants;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class MServerInfo {
public String name;
public String address;
public String playerCountLabel;
public int playerCount;
public int playercountMax;
public String label;
public long ping;
public int protocolVersion = SharedConstants.getGameVersion().getProtocolVersion();
public String version = null;
public List<Text> playerListSummary = Collections.emptyList();
@Nullable
private String icon;
public MServerInfo(String name, String address) {
this.name = name;
this.address = address;
}
@Nullable
public String getIcon() {
return this.icon;
}
public void setIcon(@Nullable String string) {
this.icon = string;
}
}

View File

@@ -0,0 +1,290 @@
package anticope.rejects.utils.server;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.NetworkState;
import net.minecraft.network.listener.ClientQueryPacketListener;
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
import net.minecraft.network.packet.c2s.query.QueryPingC2SPacket;
import net.minecraft.network.packet.c2s.query.QueryRequestC2SPacket;
import net.minecraft.network.packet.s2c.query.QueryPongS2CPacket;
import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket;
import net.minecraft.server.ServerMetadata;
import net.minecraft.text.Text;
import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class ServerListPinger {
private static final Splitter ZERO_SPLITTER = Splitter.on('\u0000').limit(6);
private static final Logger LOGGER = LogManager.getLogger();
private final List<ClientConnection> clientConnections = Collections.synchronizedList(Lists.newArrayList());
private final ArrayList<IServerFinderDisconnectListener> disconnectListeners = new ArrayList<>();
private boolean notifiedDisconnectListeners = false;
private boolean failedToConnect = true;
private static String getPlayerCountLabel(int i, int j) {
return i + "/" + j;
}
public void addServerFinderDisconnectListener(IServerFinderDisconnectListener listener) {
disconnectListeners.add(listener);
}
private void notifyDisconnectListeners() {
synchronized (this) {
if (!notifiedDisconnectListeners) {
notifiedDisconnectListeners = true;
for (IServerFinderDisconnectListener l : disconnectListeners) {
if (l != null) {
if (failedToConnect) {
l.onServerFailed();
} else {
l.onServerDisconnect();
}
}
}
}
}
}
public void add(final MServerInfo entry, final Runnable runnable) throws UnknownHostException {
Timer timeoutTimer = new Timer();
ServerAddress serverAddress = ServerAddress.parse(entry.address);
timeoutTimer.schedule(new TimerTask() {
@Override
public void run() {
notifyDisconnectListeners();
}
}, 20000);
final ClientConnection clientConnection = ClientConnection.connect(new InetSocketAddress(InetAddress.getByName(serverAddress.getAddress()), serverAddress.getPort()), false);
failedToConnect = false;
this.clientConnections.add(clientConnection);
entry.label = "multiplayer.status.pinging";
entry.ping = -1L;
entry.playerListSummary = null;
clientConnection.setPacketListener(new ClientQueryPacketListener() {
private boolean sentQuery;
private boolean received;
private long startTime;
public void onResponse(QueryResponseS2CPacket packet) {
if (this.received) {
clientConnection.disconnect(Text.translatable("multiplayer.status.unrequested"));
} else {
this.received = true;
ServerMetadata serverMetadata = packet.getServerMetadata();
if (serverMetadata.getDescription() != null) {
entry.label = serverMetadata.getDescription().getString();
} else {
entry.label = "";
}
if (serverMetadata.getVersion() != null) {
entry.version = serverMetadata.getVersion().getGameVersion();
entry.protocolVersion = serverMetadata.getVersion().getProtocolVersion();
} else {
entry.version = "multiplayer.status.old";
entry.protocolVersion = 0;
}
if (serverMetadata.getPlayers() != null) {
entry.playerCountLabel = ServerListPinger.getPlayerCountLabel(serverMetadata.getPlayers().getOnlinePlayerCount(), serverMetadata.getPlayers().getPlayerLimit());
entry.playerCount = serverMetadata.getPlayers().getOnlinePlayerCount();
entry.playercountMax = serverMetadata.getPlayers().getPlayerLimit();
List<Text> list = Lists.newArrayList();
if (ArrayUtils.isNotEmpty(serverMetadata.getPlayers().getSample())) {
GameProfile[] var4 = serverMetadata.getPlayers().getSample();
for (GameProfile gameProfile : var4) {
list.add(Text.literal(gameProfile.getName()));
}
if (serverMetadata.getPlayers().getSample().length < serverMetadata.getPlayers().getOnlinePlayerCount()) {
list.add(Text.translatable("multiplayer.status.and_more", serverMetadata.getPlayers().getOnlinePlayerCount() - serverMetadata.getPlayers().getSample().length));
}
entry.playerListSummary = list;
}
} else {
entry.playerCountLabel = "multiplayer.status.unknown";
}
String string = null;
if (serverMetadata.getFavicon() != null) {
String string2 = serverMetadata.getFavicon();
if (string2.startsWith("data:image/png;base64,")) {
string = string2.substring("data:image/png;base64," .length());
} else {
ServerListPinger.LOGGER.error("Invalid server icon (unknown format)");
}
}
if (!Objects.equals(string, entry.getIcon())) {
entry.setIcon(string);
runnable.run();
}
this.startTime = Util.getMeasuringTimeMs();
clientConnection.send(new QueryPingC2SPacket(this.startTime));
this.sentQuery = true;
notifyDisconnectListeners();
}
}
public void onPong(QueryPongS2CPacket packet) {
long l = this.startTime;
long m = Util.getMeasuringTimeMs();
entry.ping = m - l;
clientConnection.disconnect(Text.translatable("multiplayer.status.finished"));
}
public void onDisconnected(Text reason) {
if (!this.sentQuery) {
ServerListPinger.LOGGER.error("Can't ping {}: {}", entry.address, reason.getString());
entry.label = "multiplayer.status.cannot_connect";
entry.playerCountLabel = "";
entry.playerCount = 0;
entry.playercountMax = 0;
ServerListPinger.this.ping(entry);
}
notifyDisconnectListeners();
}
public ClientConnection getConnection() {
return clientConnection;
}
});
try {
clientConnection.send(new HandshakeC2SPacket(serverAddress.getAddress(), serverAddress.getPort(), NetworkState.STATUS));
clientConnection.send(new QueryRequestC2SPacket());
} catch (Throwable var6) {
LOGGER.error("Couldn't send handshake", var6);
}
}
private void ping(final MServerInfo serverInfo) {
final ServerAddress serverAddress = ServerAddress.parse(serverInfo.address);
(new Bootstrap()).group(ClientConnection.CLIENT_IO_GROUP.get()).handler(new ChannelInitializer<>() {
protected void initChannel(Channel channel) {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException var3) {
}
channel.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception {
super.channelActive(channelHandlerContext);
ByteBuf byteBuf = Unpooled.buffer();
try {
byteBuf.writeByte(254);
byteBuf.writeByte(1);
byteBuf.writeByte(250);
char[] cs = "MC|PingHost" .toCharArray();
byteBuf.writeShort(cs.length);
char[] var4 = cs;
int var5 = cs.length;
int var6;
char d;
for (var6 = 0; var6 < var5; ++var6) {
d = var4[var6];
byteBuf.writeChar(d);
}
byteBuf.writeShort(7 + 2 * serverAddress.getAddress().length());
byteBuf.writeByte(127);
cs = serverAddress.getAddress().toCharArray();
byteBuf.writeShort(cs.length);
var4 = cs;
var5 = cs.length;
for (var6 = 0; var6 < var5; ++var6) {
d = var4[var6];
byteBuf.writeChar(d);
}
byteBuf.writeInt(serverAddress.getPort());
channelHandlerContext.channel().writeAndFlush(byteBuf).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} finally {
byteBuf.release();
}
}
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) {
short s = byteBuf.readUnsignedByte();
if (s == 255) {
String string = new String(byteBuf.readBytes(byteBuf.readShort() * 2).array(), StandardCharsets.UTF_16BE);
String[] strings = Iterables.toArray(ServerListPinger.ZERO_SPLITTER.split(string), String.class);
if ("§1" .equals(strings[0])) {
String string2 = strings[2];
String string3 = strings[3];
int j = MathHelper.parseInt(strings[4], -1);
int k = MathHelper.parseInt(strings[5], -1);
serverInfo.protocolVersion = -1;
serverInfo.version = string2;
serverInfo.label = string3;
serverInfo.playerCountLabel = ServerListPinger.getPlayerCountLabel(j, k);
}
}
channelHandlerContext.close();
}
public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable) {
channelHandlerContext.close();
}
});
}
}).channel(NioSocketChannel.class).connect(serverAddress.getAddress(), serverAddress.getPort());
}
public void tick() {
synchronized (this.clientConnections) {
Iterator<ClientConnection> iterator = this.clientConnections.iterator();
while (iterator.hasNext()) {
ClientConnection clientConnection = iterator.next();
if (clientConnection.isOpen()) {
clientConnection.tick();
} else {
iterator.remove();
clientConnection.handleDisconnection();
}
}
}
}
public void cancel() {
synchronized (this.clientConnections) {
Iterator<ClientConnection> iterator = this.clientConnections.iterator();
while (iterator.hasNext()) {
ClientConnection clientConnection = iterator.next();
if (clientConnection.isOpen()) {
iterator.remove();
clientConnection.disconnect(Text.translatable("multiplayer.status.cancelled"));
}
}
}
}
}

View File

@@ -0,0 +1,213 @@
package anticope.rejects.utils.server;
import anticope.rejects.gui.servers.ServerFinderScreen;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class ServerPinger implements IServerFinderDoneListener, IServerFinderDisconnectListener {
private static final AtomicInteger threadNumber = new AtomicInteger(0);
private final Object portPingerLock = new Object();
private MServerInfo server;
private boolean done = false;
private boolean failed = false;
private Thread thread;
private int pingPort;
private ServerListPinger pinger;
private boolean notifiedDoneListeners = false;
private boolean scanPorts;
private int searchNumber;
private int currentIncrement = 1;
private boolean startingIncrement = true;
private ArrayList<IServerFinderDoneListener> doneListeners = new ArrayList<>();
private int portPingers = 0;
private int successfulPortPingers = 0;
private String pingIP;
public ServerPinger(boolean scanPorts, int searchNumber) {
pinger = new ServerListPinger();
pinger.addServerFinderDisconnectListener(this);
this.scanPorts = scanPorts;
this.searchNumber = searchNumber;
}
public void addServerFinderDoneListener(IServerFinderDoneListener listener) {
doneListeners.add(listener);
}
public void ping(String ip) {
ping(ip, 25565);
}
public int getSearchNumber() {
return searchNumber;
}
public Thread getThread() {
return thread;
}
public int getPingPort() {
return pingPort;
}
public MServerInfo getServerInfo() {
return server;
}
public void ping(String ip, int port) {
if (isOldSearch())
return;
pingIP = ip;
pingPort = port;
server = new MServerInfo("", ip + ":" + port);
server.version = null;
if (scanPorts) {
thread = new Thread(() -> pingInCurrentThread(ip, port),
"Server Pinger #" + threadNumber.incrementAndGet());
} else {
thread = new Thread(() -> pingInCurrentThread(ip, port),
"Server Pinger #" + threadNumber + ", " + port);
}
thread.start();
}
public ServerListPinger getServerListPinger() {
return pinger;
}
private boolean isOldSearch() {
return ServerFinderScreen.instance == null || ServerFinderScreen.instance.getState() == ServerFinderScreen.ServerFinderState.CANCELLED || ServerFinderScreen.getSearchNumber() != searchNumber;
}
private void runPortIncrement(String ip) {
synchronized (portPingerLock) {
portPingers = 0;
successfulPortPingers = 0;
}
for (int i = startingIncrement ? 1 : currentIncrement; i < currentIncrement * 2; i++) {
if (isOldSearch())
return;
ServerPinger pp1 = new ServerPinger(false, searchNumber);
ServerPinger pp2 = new ServerPinger(false, searchNumber);
for (IServerFinderDoneListener doneListener : doneListeners) {
pp1.addServerFinderDoneListener(doneListener);
pp2.addServerFinderDoneListener(doneListener);
}
pp1.addServerFinderDoneListener(this);
pp2.addServerFinderDoneListener(this);
if (ServerFinderScreen.instance != null && !isOldSearch()) {
ServerFinderScreen.instance.incrementTargetChecked(2);
}
pp1.ping(ip, 25565 - i);
pp2.ping(ip, 25565 + i);
}
synchronized (portPingerLock) {
currentIncrement *= 2;
}
}
private void pingInCurrentThread(String ip, int port) {
if (isOldSearch())
return;
try {
pinger.add(server, () -> {
});
} catch (Exception e) {
failed = true;
}
startingIncrement = true;
if (!failed) {
currentIncrement = 8;
}
if (!failed && scanPorts) {
runPortIncrement(ip);
}
if (failed) {
pinger.cancel();
done = true;
notifyDoneListeners(false);
}
}
public boolean isStillPinging() {
return !done;
}
public boolean isWorking() {
return !failed;
}
public boolean isOtherVersion() {
return server.protocolVersion != 47;
}
public String getServerIP() {
return server.address;
}
@Override
public void onServerDisconnect() {
if (isOldSearch())
return;
pinger.cancel();
done = true;
notifyDoneListeners(false);
}
private void notifyDoneListeners(boolean failure) {
synchronized (this) {
if (!notifiedDoneListeners) {
notifiedDoneListeners = true;
for (IServerFinderDoneListener doneListener : doneListeners) {
if (doneListener != null) {
if (failure) {
doneListener.onServerFailed(this);
} else {
doneListener.onServerDone(this);
}
}
}
}
}
}
@Override
public void onServerFailed() {
if (isOldSearch())
return;
pinger.cancel();
done = true;
notifyDoneListeners(true);
}
@Override
public void onServerDone(ServerPinger pinger) {
synchronized (portPingerLock) {
portPingers += 1;
if (pinger.isWorking())
successfulPortPingers += 1;
if (portPingers == (startingIncrement ? currentIncrement * 2 - 2 : currentIncrement) && currentIncrement <= 5000 && successfulPortPingers > 0) {
startingIncrement = false;
new Thread(() -> runPortIncrement(pingIP)).start();
}
}
}
@Override
public void onServerFailed(ServerPinger pinger) {
synchronized (portPingerLock) {
portPingers += 1;
}
}
}