QueryThreadGs4.java
net.minecraft.server.rcon.thread.QueryThreadGs4
信息
- 全限定名:net.minecraft.server.rcon.thread.QueryThreadGs4
- 类型:public class
- 包:net.minecraft.server.rcon.thread
- 源码路径:src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java
- 起始行号:L25
- 继承:GenericThread
- 职责:
TODO
字段/常量
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L26 - 说明:
TODO
- 类型:
-
GAME_TYPE- 类型:
String - 修饰符:
private static final - 源码定位:
L27 - 说明:
TODO
- 类型:
-
GAME_ID- 类型:
String - 修饰符:
private static final - 源码定位:
L28 - 说明:
TODO
- 类型:
-
CHALLENGE_CHECK_INTERVAL- 类型:
long - 修饰符:
private static final - 源码定位:
L29 - 说明:
TODO
- 类型:
-
RESPONSE_CACHE_TIME- 类型:
long - 修饰符:
private static final - 源码定位:
L30 - 说明:
TODO
- 类型:
-
lastChallengeCheck- 类型:
long - 修饰符:
private - 源码定位:
L31 - 说明:
TODO
- 类型:
-
port- 类型:
int - 修饰符:
private final - 源码定位:
L32 - 说明:
TODO
- 类型:
-
serverPort- 类型:
int - 修饰符:
private final - 源码定位:
L33 - 说明:
TODO
- 类型:
-
maxPlayers- 类型:
int - 修饰符:
private final - 源码定位:
L34 - 说明:
TODO
- 类型:
-
serverName- 类型:
String - 修饰符:
private final - 源码定位:
L35 - 说明:
TODO
- 类型:
-
worldName- 类型:
String - 修饰符:
private final - 源码定位:
L36 - 说明:
TODO
- 类型:
-
socket- 类型:
DatagramSocket - 修饰符:
private - 源码定位:
L37 - 说明:
TODO
- 类型:
-
buffer- 类型:
byte[] - 修饰符:
private final - 源码定位:
L38 - 说明:
TODO
- 类型:
-
hostIp- 类型:
String - 修饰符:
private - 源码定位:
L39 - 说明:
TODO
- 类型:
-
serverIp- 类型:
String - 修饰符:
private - 源码定位:
L40 - 说明:
TODO
- 类型:
-
validChallenges- 类型:
Map<SocketAddress,QueryThreadGs4.RequestChallenge> - 修饰符:
private final - 源码定位:
L41 - 说明:
TODO
- 类型:
-
rulesResponse- 类型:
NetworkDataOutputStream - 修饰符:
private final - 源码定位:
L42 - 说明:
TODO
- 类型:
-
lastRulesResponse- 类型:
long - 修饰符:
private - 源码定位:
L43 - 说明:
TODO
- 类型:
-
serverInterface- 类型:
ServerInterface - 修饰符:
private final - 源码定位:
L44 - 说明:
TODO
- 类型:
内部类/嵌套类型
net.minecraft.server.rcon.thread.QueryThreadGs4.RequestChallenge- 类型:
class - 修饰符:
private static - 源码定位:
L269 - 说明:
TODO
- 类型:
构造器
private QueryThreadGs4(ServerInterface serverInterface, int port) @ L46
- 构造器名:QueryThreadGs4
- 源码定位:L46
- 修饰符:private
参数:
- serverInterface: ServerInterface
- port: int
说明:
TODO
方法
下面的方法块按源码顺序生成。
public static QueryThreadGs4 create(ServerInterface serverInterface) @ L74
- 方法名:create
- 源码定位:L74
- 返回类型:QueryThreadGs4
- 修饰符:public static
参数:
- serverInterface: ServerInterface
说明:
TODO
private void sendTo(byte[] data, DatagramPacket src) @ L85
- 方法名:sendTo
- 源码定位:L85
- 返回类型:void
- 修饰符:private
参数:
- data: byte[]
- src: DatagramPacket
说明:
TODO
private boolean processPacket(DatagramPacket packet) @ L89
- 方法名:processPacket
- 源码定位:L89
- 返回类型:boolean
- 修饰符:private
参数:
- packet: DatagramPacket
说明:
TODO
private byte[] buildRuleResponse(DatagramPacket packet) @ L131
- 方法名:buildRuleResponse
- 源码定位:L131
- 返回类型:byte[]
- 修饰符:private
参数:
- packet: DatagramPacket
说明:
TODO
private byte[] getIdentBytes(SocketAddress src) @ L184
- 方法名:getIdentBytes
- 源码定位:L184
- 返回类型:byte[]
- 修饰符:private
参数:
- src: SocketAddress
说明:
TODO
private Boolean validChallenge(DatagramPacket src) @ L188
- 方法名:validChallenge
- 源码定位:L188
- 返回类型:Boolean
- 修饰符:private
参数:
- src: DatagramPacket
说明:
TODO
private void sendChallenge(DatagramPacket src) @ L198
- 方法名:sendChallenge
- 源码定位:L198
- 返回类型:void
- 修饰符:private
参数:
- src: DatagramPacket
说明:
TODO
private void pruneChallenges() @ L204
- 方法名:pruneChallenges
- 源码定位:L204
- 返回类型:void
- 修饰符:private
参数:
- 无
说明:
TODO
public void run() @ L214
- 方法名:run
- 源码定位:L214
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public boolean start() @ L239
- 方法名:start
- 源码定位:L239
- 返回类型:boolean
- 修饰符:public
参数:
- 无
说明:
TODO
private void recoverSocketError(Exception e) @ L248
- 方法名:recoverSocketError
- 源码定位:L248
- 返回类型:void
- 修饰符:private
参数:
- e: Exception
说明:
TODO
private boolean initSocket() @ L258
- 方法名:initSocket
- 源码定位:L258
- 返回类型:boolean
- 修饰符:private
参数:
- 无
说明:
TODO
代码
public class QueryThreadGs4 extends GenericThread {
private static final Logger LOGGER = LogUtils.getLogger();
private static final String GAME_TYPE = "SMP";
private static final String GAME_ID = "MINECRAFT";
private static final long CHALLENGE_CHECK_INTERVAL = 30000L;
private static final long RESPONSE_CACHE_TIME = 5000L;
private long lastChallengeCheck;
private final int port;
private final int serverPort;
private final int maxPlayers;
private final String serverName;
private final String worldName;
private DatagramSocket socket;
private final byte[] buffer = new byte[1460];
private String hostIp;
private String serverIp;
private final Map<SocketAddress, QueryThreadGs4.RequestChallenge> validChallenges;
private final NetworkDataOutputStream rulesResponse;
private long lastRulesResponse;
private final ServerInterface serverInterface;
private QueryThreadGs4(ServerInterface serverInterface, int port) {
super("Query Listener");
this.serverInterface = serverInterface;
this.port = port;
this.serverIp = serverInterface.getServerIp();
this.serverPort = serverInterface.getServerPort();
this.serverName = serverInterface.getServerName();
this.maxPlayers = serverInterface.getMaxPlayers();
this.worldName = serverInterface.getLevelIdName();
this.lastRulesResponse = 0L;
this.hostIp = "0.0.0.0";
if (!this.serverIp.isEmpty() && !this.hostIp.equals(this.serverIp)) {
this.hostIp = this.serverIp;
} else {
this.serverIp = "0.0.0.0";
try {
InetAddress addr = InetAddress.getLocalHost();
this.hostIp = addr.getHostAddress();
} catch (UnknownHostException var4) {
LOGGER.warn("Unable to determine local host IP, please set server-ip in server.properties", (Throwable)var4);
}
}
this.rulesResponse = new NetworkDataOutputStream(1460);
this.validChallenges = Maps.newHashMap();
}
public static @Nullable QueryThreadGs4 create(ServerInterface serverInterface) {
int port = serverInterface.getProperties().queryPort;
if (0 < port && 65535 >= port) {
QueryThreadGs4 result = new QueryThreadGs4(serverInterface, port);
return !result.start() ? null : result;
} else {
LOGGER.warn("Invalid query port {} found in server.properties (queries disabled)", port);
return null;
}
}
private void sendTo(byte[] data, DatagramPacket src) throws IOException {
this.socket.send(new DatagramPacket(data, data.length, src.getSocketAddress()));
}
private boolean processPacket(DatagramPacket packet) throws IOException {
byte[] buf = packet.getData();
int len = packet.getLength();
SocketAddress socketAddress = packet.getSocketAddress();
LOGGER.debug("Packet len {} [{}]", len, socketAddress);
if (3 <= len && -2 == buf[0] && -3 == buf[1]) {
LOGGER.debug("Packet '{}' [{}]", PktUtils.toHexString(buf[2]), socketAddress);
switch (buf[2]) {
case 0:
if (!this.validChallenge(packet)) {
LOGGER.debug("Invalid challenge [{}]", socketAddress);
return false;
} else if (15 == len) {
this.sendTo(this.buildRuleResponse(packet), packet);
LOGGER.debug("Rules [{}]", socketAddress);
} else {
NetworkDataOutputStream dos = new NetworkDataOutputStream(1460);
dos.write(0);
dos.writeBytes(this.getIdentBytes(packet.getSocketAddress()));
dos.writeString(this.serverName);
dos.writeString("SMP");
dos.writeString(this.worldName);
dos.writeString(Integer.toString(this.serverInterface.getPlayerCount()));
dos.writeString(Integer.toString(this.maxPlayers));
dos.writeShort((short)this.serverPort);
dos.writeString(this.hostIp);
this.sendTo(dos.toByteArray(), packet);
LOGGER.debug("Status [{}]", socketAddress);
}
default:
return true;
case 9:
this.sendChallenge(packet);
LOGGER.debug("Challenge [{}]", socketAddress);
return true;
}
} else {
LOGGER.debug("Invalid packet [{}]", socketAddress);
return false;
}
}
private byte[] buildRuleResponse(DatagramPacket packet) throws IOException {
long now = Util.getMillis();
if (now < this.lastRulesResponse + 5000L) {
byte[] data = this.rulesResponse.toByteArray();
byte[] ident = this.getIdentBytes(packet.getSocketAddress());
data[1] = ident[0];
data[2] = ident[1];
data[3] = ident[2];
data[4] = ident[3];
return data;
} else {
this.lastRulesResponse = now;
this.rulesResponse.reset();
this.rulesResponse.write(0);
this.rulesResponse.writeBytes(this.getIdentBytes(packet.getSocketAddress()));
this.rulesResponse.writeString("splitnum");
this.rulesResponse.write(128);
this.rulesResponse.write(0);
this.rulesResponse.writeString("hostname");
this.rulesResponse.writeString(this.serverName);
this.rulesResponse.writeString("gametype");
this.rulesResponse.writeString("SMP");
this.rulesResponse.writeString("game_id");
this.rulesResponse.writeString("MINECRAFT");
this.rulesResponse.writeString("version");
this.rulesResponse.writeString(this.serverInterface.getServerVersion());
this.rulesResponse.writeString("plugins");
this.rulesResponse.writeString(this.serverInterface.getPluginNames());
this.rulesResponse.writeString("map");
this.rulesResponse.writeString(this.worldName);
this.rulesResponse.writeString("numplayers");
this.rulesResponse.writeString(this.serverInterface.getPlayerCount() + "");
this.rulesResponse.writeString("maxplayers");
this.rulesResponse.writeString(this.maxPlayers + "");
this.rulesResponse.writeString("hostport");
this.rulesResponse.writeString(this.serverPort + "");
this.rulesResponse.writeString("hostip");
this.rulesResponse.writeString(this.hostIp);
this.rulesResponse.write(0);
this.rulesResponse.write(1);
this.rulesResponse.writeString("player_");
this.rulesResponse.write(0);
String[] players = this.serverInterface.getPlayerNames();
for (String player : players) {
this.rulesResponse.writeString(player);
}
this.rulesResponse.write(0);
return this.rulesResponse.toByteArray();
}
}
private byte[] getIdentBytes(SocketAddress src) {
return this.validChallenges.get(src).getIdentBytes();
}
private Boolean validChallenge(DatagramPacket src) {
SocketAddress sockAddr = src.getSocketAddress();
if (!this.validChallenges.containsKey(sockAddr)) {
return false;
} else {
byte[] data = src.getData();
return this.validChallenges.get(sockAddr).getChallenge() == PktUtils.intFromNetworkByteArray(data, 7, src.getLength());
}
}
private void sendChallenge(DatagramPacket src) throws IOException {
QueryThreadGs4.RequestChallenge challenge = new QueryThreadGs4.RequestChallenge(src);
this.validChallenges.put(src.getSocketAddress(), challenge);
this.sendTo(challenge.getChallengeBytes(), src);
}
private void pruneChallenges() {
if (this.running) {
long now = Util.getMillis();
if (now >= this.lastChallengeCheck + 30000L) {
this.lastChallengeCheck = now;
this.validChallenges.values().removeIf(challenge -> challenge.before(now));
}
}
}
@Override
public void run() {
LOGGER.info("Query running on {}:{}", this.serverIp, this.port);
this.lastChallengeCheck = Util.getMillis();
DatagramPacket request = new DatagramPacket(this.buffer, this.buffer.length);
try {
while (this.running) {
try {
this.socket.receive(request);
this.pruneChallenges();
this.processPacket(request);
} catch (SocketTimeoutException var8) {
this.pruneChallenges();
} catch (PortUnreachableException var9) {
} catch (IOException var10) {
this.recoverSocketError(var10);
}
}
} finally {
LOGGER.debug("closeSocket: {}:{}", this.serverIp, this.port);
this.socket.close();
}
}
@Override
public boolean start() {
if (this.running) {
return true;
} else {
return !this.initSocket() ? false : super.start();
}
}
private void recoverSocketError(Exception e) {
if (this.running) {
LOGGER.warn("Unexpected exception", (Throwable)e);
if (!this.initSocket()) {
LOGGER.error("Failed to recover from exception, shutting down!");
this.running = false;
}
}
}
private boolean initSocket() {
try {
this.socket = new DatagramSocket(this.port, InetAddress.getByName(this.serverIp));
this.socket.setSoTimeout(500);
return true;
} catch (Exception var2) {
LOGGER.warn("Unable to initialise query system on {}:{}", this.serverIp, this.port, var2);
return false;
}
}
private static class RequestChallenge {
private final long time = new Date().getTime();
private final int challenge;
private final byte[] identBytes;
private final byte[] challengeBytes;
private final String ident;
public RequestChallenge(DatagramPacket src) {
byte[] buf = src.getData();
this.identBytes = new byte[4];
this.identBytes[0] = buf[3];
this.identBytes[1] = buf[4];
this.identBytes[2] = buf[5];
this.identBytes[3] = buf[6];
this.ident = new String(this.identBytes, StandardCharsets.UTF_8);
this.challenge = RandomSource.createThreadLocalInstance().nextInt(16777216);
this.challengeBytes = String.format(Locale.ROOT, "\t%s%d\u0000", this.ident, this.challenge).getBytes(StandardCharsets.UTF_8);
}
public Boolean before(long time) {
return this.time < time;
}
public int getChallenge() {
return this.challenge;
}
public byte[] getChallengeBytes() {
return this.challengeBytes;
}
public byte[] getIdentBytes() {
return this.identBytes;
}
public String getIdent() {
return this.ident;
}
}
}引用的其他类
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
NetworkDataOutputStream()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
PktUtils.intFromNetworkByteArray(), PktUtils.toHexString()
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
RandomSource.createThreadLocalInstance()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.getMillis()
- 引用位置: