no message
All checks were successful
Gitea Actions Build / Build (push) Successful in 51s

This commit is contained in:
Armamem0t 2025-07-09 23:08:46 +08:00
parent 07150a4827
commit 9796338c72
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
6 changed files with 473 additions and 15 deletions

View File

@ -38,6 +38,8 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.jetbrains:annotations:24.0.0") implementation("org.jetbrains:annotations:24.0.0")
annotationProcessor("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 { tasks.test {

View File

@ -1,5 +1,5 @@
GROUPSID=com.mingliqiye GROUPSID=com.mingliqiye
ARTIFACTID=socket-utilts ARTIFACTID=minecraft-SLP
VERSIONS=1.0.1 VERSIONS=1.0.1
MAINCLASS=com.mingliqiye.Main MAINCLASS=com.mingliqiye.minecraftSLP.Main
JDKVERSIONS=1.8 JDKVERSIONS=1.8

View File

@ -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);
}
}
}

View 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);
}
}
}

View File

@ -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 : ""
);
}
}

View 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);
}
}