generated from mingliqiye/lib-tem
This commit is contained in:
parent
07150a4827
commit
9796338c72
@ -38,6 +38,8 @@ dependencies {
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
implementation("org.jetbrains:annotations:24.0.0")
|
||||
annotationProcessor("org.jetbrains:annotations:24.0.0")
|
||||
implementation("com.alibaba.fastjson2:fastjson2:2.0.57")
|
||||
implementation("com.mingliqiye:network-endpoint:1.0.3")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
@ -1,5 +1,5 @@
|
||||
GROUPSID=com.mingliqiye
|
||||
ARTIFACTID=socket-utilts
|
||||
ARTIFACTID=minecraft-SLP
|
||||
VERSIONS=1.0.1
|
||||
MAINCLASS=com.mingliqiye.Main
|
||||
MAINCLASS=com.mingliqiye.minecraftSLP.Main
|
||||
JDKVERSIONS=1.8
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.mingliqiye;
|
||||
|
||||
public class Main {
|
||||
/**
|
||||
* @param args []
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
System.out.print("Hello and welcome!");
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
System.out.println("i = " + i);
|
||||
}
|
||||
}
|
||||
}
|
69
src/main/java/com/mingliqiye/minecraftSLP/Main.java
Normal file
69
src/main/java/com/mingliqiye/minecraftSLP/Main.java
Normal file
@ -0,0 +1,69 @@
|
||||
package com.mingliqiye.minecraftSLP;
|
||||
|
||||
import com.mingliqiye.network.endpoint.NetworkEndpoint;
|
||||
import com.mingliqiye.network.endpoint.NetworkException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 1) {
|
||||
System.err.println("请使用 {地址}:{端口号}");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
NetworkEndpoint networkEndpoints = NetworkEndpoint.of(args[0]);
|
||||
MinecraftServerStatus minecraftServerStatus = SLP.getServerStatus(
|
||||
networkEndpoints
|
||||
);
|
||||
System.out.printf(
|
||||
"服务器 %s %n",
|
||||
networkEndpoints.toHostPortString()
|
||||
);
|
||||
System.out.printf(
|
||||
"版本号 %s 协议版本 %s %n",
|
||||
minecraftServerStatus.getVersion().getName(),
|
||||
minecraftServerStatus.getVersion().getProtocol()
|
||||
);
|
||||
System.out.printf(
|
||||
"描述信息 %s %n",
|
||||
minecraftServerStatus.getDescription().getText()
|
||||
);
|
||||
System.out.printf(
|
||||
"玩家数 %s/%s %n",
|
||||
minecraftServerStatus.getPlayers().getOnline(),
|
||||
minecraftServerStatus.getPlayers().getMax()
|
||||
);
|
||||
if (minecraftServerStatus.getPlayers().getOnline() != 0) {
|
||||
System.out.println("在线玩家:");
|
||||
minecraftServerStatus
|
||||
.getPlayers()
|
||||
.getSample()
|
||||
.forEach(v -> {
|
||||
System.out.printf(
|
||||
"玩家 %s UUID %s %n",
|
||||
v.getName(),
|
||||
v.getId()
|
||||
);
|
||||
});
|
||||
}
|
||||
System.out.println("源数据:");
|
||||
System.out.println(minecraftServerStatus.getJsonData());
|
||||
} catch (NetworkException e) {
|
||||
System.err.printf(
|
||||
"服务器地址 %s DNS 查询失败 %s %s %n",
|
||||
args[0],
|
||||
e.getClass().getName(),
|
||||
e.getMessage()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
System.err.printf(
|
||||
"%s 查询失败 %s %s %n",
|
||||
args[0],
|
||||
e.getClass().getName(),
|
||||
e.getMessage()
|
||||
);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
package com.mingliqiye.minecraftSLP;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MinecraftServerStatus {
|
||||
|
||||
private Description description;
|
||||
private Players players;
|
||||
private Version version;
|
||||
private String favicon;
|
||||
private boolean enforcesSecureChat;
|
||||
private boolean previewsChat;
|
||||
private String jsonData;
|
||||
|
||||
// Getters and Setters
|
||||
public Description getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(Description description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Players getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void setPlayers(Players players) {
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getFavicon() {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
public void setFavicon(String favicon) {
|
||||
this.favicon = favicon;
|
||||
}
|
||||
|
||||
public boolean isEnforcesSecureChat() {
|
||||
return enforcesSecureChat;
|
||||
}
|
||||
|
||||
public void setEnforcesSecureChat(boolean enforcesSecureChat) {
|
||||
this.enforcesSecureChat = enforcesSecureChat;
|
||||
}
|
||||
|
||||
public boolean isPreviewsChat() {
|
||||
return previewsChat;
|
||||
}
|
||||
|
||||
public void setPreviewsChat(boolean previewsChat) {
|
||||
this.previewsChat = previewsChat;
|
||||
}
|
||||
|
||||
public String getJsonData() {
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public void setJsonData(String jsonData) {
|
||||
this.jsonData = jsonData;
|
||||
}
|
||||
|
||||
// Nested Classes
|
||||
public static class Description {
|
||||
|
||||
private String text;
|
||||
private Extra[] extra;
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public List<Extra> getExtra() {
|
||||
return Arrays.asList(extra);
|
||||
}
|
||||
|
||||
public void setExtra(Extra[] extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public static class Extra {
|
||||
|
||||
private String text;
|
||||
private String color;
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
|
||||
// 其他样式字段...
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Players {
|
||||
|
||||
private int max;
|
||||
private int online;
|
||||
private PlayerSample[] sample;
|
||||
|
||||
public int getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax(int max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public int getOnline() {
|
||||
return online;
|
||||
}
|
||||
|
||||
public void setOnline(int online) {
|
||||
this.online = online;
|
||||
}
|
||||
|
||||
public List<PlayerSample> getSample() {
|
||||
return Arrays.asList(sample);
|
||||
}
|
||||
|
||||
public void setSample(PlayerSample[] sample) {
|
||||
this.sample = sample;
|
||||
}
|
||||
|
||||
public static class PlayerSample {
|
||||
|
||||
private String name;
|
||||
private String id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"Player(name=%s,id=%s)",
|
||||
getName(),
|
||||
getId()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Version {
|
||||
|
||||
private String name;
|
||||
private int protocol;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(int protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
}
|
||||
|
||||
// 实用方法
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"Version: %s (Protocol %d), Players: %d/%d, Description: %s",
|
||||
version != null ? version.name : "?",
|
||||
version != null ? version.protocol : 0,
|
||||
players != null ? players.online : 0,
|
||||
players != null ? players.max : 0,
|
||||
description != null ? description.text : ""
|
||||
);
|
||||
}
|
||||
}
|
166
src/main/java/com/mingliqiye/minecraftSLP/SLP.java
Normal file
166
src/main/java/com/mingliqiye/minecraftSLP/SLP.java
Normal file
@ -0,0 +1,166 @@
|
||||
package com.mingliqiye.minecraftSLP;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.mingliqiye.network.endpoint.NetworkEndpoint;
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class SLP {
|
||||
|
||||
/**
|
||||
* 将 int32 4个字节 转换为 short 2 个字节
|
||||
* @param value int32
|
||||
* @return short
|
||||
*/
|
||||
public static byte[] toUnsignedShort(int value) {
|
||||
byte[] array = new byte[2];
|
||||
ByteBuffer.wrap(array, 0, 2)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putShort((short) (value & 0xFFFF));
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 和服务器握手使用的字节
|
||||
* @param serverIP 服务器地址
|
||||
* @param serverPort 服务器端口号
|
||||
* @param type 类型 1为 Status
|
||||
* @return 字节数组
|
||||
*/
|
||||
public static byte[] getHandshakePack(
|
||||
String serverIP,
|
||||
int serverPort,
|
||||
int type
|
||||
) throws IOException {
|
||||
ByteArrayOutputStream pack = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream byteArrayOutputStream =
|
||||
new ByteArrayOutputStream();
|
||||
pack.write(0x00);
|
||||
pack.write(toVarInt(1156));
|
||||
byte[] sip = serverIP.getBytes();
|
||||
pack.write(toVarInt(sip.length));
|
||||
pack.write(sip);
|
||||
pack.write(toUnsignedShort(serverPort));
|
||||
pack.write(toVarInt(type));
|
||||
byteArrayOutputStream.write(toVarInt(pack.size()));
|
||||
byteArrayOutputStream.write(pack.toByteArray());
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息的字节 是固定值
|
||||
* @return 信息的字节
|
||||
*/
|
||||
public static byte[] getStatusPack() {
|
||||
return new byte[] { 0x01, 0x00 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 从输入流中读取 服务器信息
|
||||
* @param inputStream 输入流
|
||||
* @return 服务器信息实体
|
||||
* @throws IOException 读取错误
|
||||
*/
|
||||
public static MinecraftServerStatus getStatusJsonEntity(
|
||||
DataInputStream inputStream
|
||||
) throws IOException {
|
||||
readVarInt(inputStream);
|
||||
inputStream.readByte();
|
||||
int lengthjson = readVarInt(inputStream);
|
||||
byte[] data = new byte[lengthjson];
|
||||
inputStream.readFully(data);
|
||||
MinecraftServerStatus serverStatus = JSON.parseObject(
|
||||
data,
|
||||
MinecraftServerStatus.class
|
||||
);
|
||||
serverStatus.setJsonData(new String(data));
|
||||
return serverStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从输入流中读取 varint 1-5字节
|
||||
* @param in 输入流
|
||||
* @return int
|
||||
* @throws IOException 读取错误
|
||||
*/
|
||||
public static int readVarInt(DataInputStream in) throws IOException {
|
||||
int value = 0;
|
||||
int length = 0;
|
||||
byte currentByte;
|
||||
do {
|
||||
currentByte = in.readByte();
|
||||
value |= (currentByte & 0x7F) << (length * 7);
|
||||
length += 1;
|
||||
if (length > 5) {
|
||||
throw new RuntimeException("VarInt too long");
|
||||
}
|
||||
} while ((currentByte & 0x80) != 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int32转换为 1-5个字节的 varint
|
||||
* @param value int32
|
||||
* @return varint 字节数组
|
||||
*/
|
||||
public static byte[] toVarInt(int value) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
while (true) {
|
||||
if ((value & 0xFFFFFF80) == 0) {
|
||||
buffer.write(value); // 写入单个字节
|
||||
break;
|
||||
}
|
||||
buffer.write((value & 0x7F) | 0x80); // 写入字节并设置继续位
|
||||
value >>>= 7;
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
public static Socket getNewConnect(NetworkEndpoint networkEndpoint)
|
||||
throws IOException {
|
||||
Socket socket = new Socket();
|
||||
socket.setSoTimeout(5000);
|
||||
socket.connect(networkEndpoint.toInetSocketAddress());
|
||||
return socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s 域名IP+端口号字符串 示例 "127.0.0.1:25565"
|
||||
* @return MinecraftServerStatus 服务器回执状态实体
|
||||
* @throws IOException 网络错误
|
||||
*/
|
||||
public static MinecraftServerStatus getServerStatus(String s)
|
||||
throws IOException {
|
||||
return getServerStatus(NetworkEndpoint.of(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s 域名IP
|
||||
* @param i 端口号
|
||||
* @return MinecraftServerStatus 服务器回执状态实体
|
||||
* @throws IOException 网络错误
|
||||
*/
|
||||
public static MinecraftServerStatus getServerStatus(String s, Integer i)
|
||||
throws IOException {
|
||||
return getServerStatus(NetworkEndpoint.of(s, i));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param e 域名IP+端口号实例
|
||||
* @return MinecraftServerStatus 服务器回执状态实体
|
||||
* @throws IOException 网络错误
|
||||
* @see NetworkEndpoint
|
||||
*/
|
||||
public static MinecraftServerStatus getServerStatus(NetworkEndpoint e)
|
||||
throws IOException {
|
||||
Socket socket = getNewConnect(e);
|
||||
OutputStream out = socket.getOutputStream();
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
out.write(getHandshakePack(e.getHost(), e.getPort(), 1));
|
||||
out.write(getStatusPack());
|
||||
return getStatusJsonEntity(in);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user