Skip to content

Commit

Permalink
feat: 删除之前依赖的登录协议库 改成自己实现简单获取服务器信息
Browse files Browse the repository at this point in the history
build: 1.2.0
  • Loading branch information
limbang committed Dec 27, 2023
1 parent 11592ce commit 5f08207
Show file tree
Hide file tree
Showing 23 changed files with 775 additions and 434 deletions.
33 changes: 2 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
[![](https://img.shields.io/github/v/release/limbang/mirai-console-minecraft-plugin?include_prereleases)](https://github.com/limbang/mirai-console-minecraft-plugin/releases)
![](https://img.shields.io/github/downloads/limbang/mirai-console-minecraft-plugin/total)
[![](https://img.shields.io/github/license/limbang/mirai-console-minecraft-plugin)](https://github.com/limbang/mirai-console-minecraft-plugin/blob/master/LICENSE)
[![](https://img.shields.io/badge/mirai-2.12.0-69c1b9)](https://github.com/mamoe/mirai)
[![](https://img.shields.io/badge/mirai-2.16.0-69c1b9)](https://github.com/mamoe/mirai)

本项目是基于 Mirai Console 编写的插件
<p>用于 ping 服务器状态,和查看服务器 tps,基于<a href = "https://github.com/Forsaken-Land/doctor">doctor</a>库实现</p>
<p>用于 ping 服务器状态</p>
<p>戳一戳机器人头像可以获取帮助</p>
</div>

Expand All @@ -15,29 +15,10 @@
## 命令

```shell
/mc addLogin <name> <authServerUrl> <sessionServerUrl> <username> <password> # 添加登陆信息
/mc addServer <name> <address> [port] # 添加服务器,端口默认 25565
/mc addServerLogin <loginName> <name> <address> [port] # 添加带登陆信息带服务器,端口默认 25565
/mc deleteLogin <name> # 删除登陆信息
/mc deleteServer <name> # 删除服务器
/mc loginInfo # 查看登陆信息
/mc setTps <value> # 设置tps功能启用
/mc setAllToImg <value> # 设置All消息转换为图片功能是否启动
```
### 使用TPS功能
1. 使用指令添加登陆信息,比如添加遗落之地的皮肤站演示如下
```shell
/mc addLogin nano https://skin.blackyin.xyz/api/yggdrasil/authserver https://skin.blackyin.xyz/api/yggdrasil/sessionserver 账号 密码
```
2. 然后添加服务器的时候用,`addServerLogin`命令添加,loginName 就是刚才设置的 nano,就可以使用了

mc addLogin url参考 [yggdrasil](https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#%E4%BC%9A%E8%AF%9D%E9%83%A8%E5%88%86)

- name 登陆配置名称
- authServerUrl 验证服务器地址 正版地址为:https://authserver.mojang.com
- sessionServerUrl 会话服务器地址 正版地址为:https://sessionserver.mojang.com
- username 账号
- password 密码

```shell
# 设置触发指令
Expand All @@ -46,24 +27,14 @@ mc addLogin url参考 [yggdrasil](https://github.com/yushijinhun/authlib-injecto
name 可设置如下
- PING `ping服务器`
- LIST `查询列表`
- TPS `查询tps`
- PING_ALL `ping全部服务器`


## 版本支持

TPS 暂时只支持 Forge 端

----

## 功能展示

戳一戳功能:
![](img/Screenshot_20220319_195629.jpg)

tps 功能:
![](img/1704DCA5-EC7F-4EF9-BF80-10DAC604836D.png)

直 ping 地址功能:
![](img/ABCBBD85-E183-41FE-BA3A-9D88853F43B3.png)

Expand Down
11 changes: 3 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
plugins {
val kotlinVersion = "1.8.10"
val kotlinVersion = "1.9.0"
kotlin("jvm") version kotlinVersion
kotlin("plugin.serialization") version kotlinVersion

id("net.mamoe.mirai-console") version "2.14.0"
id("net.mamoe.mirai-console") version "2.16.0"
}

group = "top.limbang"
version = "1.1.13"
version = "1.2.0"


repositories {
mavenCentral()
maven("https://jitpack.io")
}

dependencies {
implementation("top.fanua.doctor:doctor-client:1.3.13")
implementation("top.fanua.doctor:doctor-plugin-forge-fix:1.3.13")
compileOnly("top.limbang:mirai-plugin-general-interface:1.0.2")
testImplementation("org.slf4j:slf4j-simple:2.0.5")
"shadowLink"("top.fanua.doctor:doctor-client")
"shadowLink"("top.fanua.doctor:doctor-plugin-forge-fix")
}
169 changes: 169 additions & 0 deletions src/main/kotlin/ServerListPing.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (c) 2023 limbang and contributors.
*
* 此源代码的使用受 GNU AGPLv3 许可证的约束,该许可证可在"LICENSE"文件中找到。
* Use of this source code is governed by the GNU AGPLv3 license that can be found in the "LICENSE" file.
*/

package top.limbang.minecraft

import kotlinx.serialization.json.*
import top.limbang.minecraft.entity.Player
import top.limbang.minecraft.entity.PlayerInfo
import top.limbang.minecraft.entity.ServerStatus
import top.limbang.minecraft.network.MinecraftInputStream
import top.limbang.minecraft.network.MinecraftOutputStream
import top.limbang.minecraft.utlis.getForgeDate
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
import java.net.Socket


fun ping(host: String, port: Int) = MinecraftClient.ping(host, port)

object MinecraftClient {

/**
* Ping
*
* @param host 地址
* @param port 端口
* @return 延迟 和 服务器状态
*/
fun ping(host: String, port: Int): Pair<Int, ServerStatus> {
Socket(host, port).use { socket ->
val inStream = MinecraftInputStream(DataInputStream(socket.getInputStream()))
val outStream = MinecraftOutputStream(DataOutputStream(socket.getOutputStream()))

// 发送握手包
val handshake = encodeHandshake(address = host, port = port)
outStream.sendPacket(handshake)
// 发送状态请求数据包
outStream.writeVarInt(0x01)
outStream.writeByte(0x00)

// 读取握手包响应
val jsonString = decodeHandshakeResponse(inStream)
// 获取ping延时
val delay = getPingDelay(inStream, outStream)
val serverStatus = handleServerStatusJson(jsonString)
//decodeFavicon(serverStatus.favicon)
return Pair(delay,serverStatus)
}
}

/**
* 处理服务器状态JSON
*
* @param json
* @return
*/
fun handleServerStatusJson(json: String): ServerStatus {
val element = Json.parseToJsonElement(json)
// 解析版本信息
val versionObject = element.jsonObject["version"] ?: throw RuntimeException("未找到版本信息...")
val versionName = versionObject.jsonObject["name"]!!.jsonPrimitive.content
val versionNumber = versionObject.jsonObject["protocol"]!!.jsonPrimitive.int
// 解析玩家信息
val playerObject = element.jsonObject["players"]!!
val playerMax = playerObject.jsonObject["max"]!!.jsonPrimitive.int
val playerOnline = playerObject.jsonObject["online"]!!.jsonPrimitive.int
val players = mutableListOf<Player>()
playerObject.jsonObject["sample"]?.jsonArray?.forEach {
players.add(
Player(
id = it.jsonObject["id"]!!.jsonPrimitive.content,
name = it.jsonObject["name"]!!.jsonPrimitive.content
)
)
}
val playerInfo = PlayerInfo(playerMax = playerMax, playerOnline = playerOnline, players = players)
// 解析服务器头像
val favicon = element.jsonObject["favicon"]?.jsonPrimitive?.content ?: ""
// 解析描述
val description = if (versionNumber == 5) element.jsonObject["description"]!!.jsonPrimitive.content
else element.jsonObject["description"]!!.jsonObject["text"]!!.jsonPrimitive.content

return ServerStatus(
favicon = favicon,
description = description,
playerInfo = playerInfo,
versionName = versionName,
versionNumber = versionNumber,
forgeData = getForgeDate(element, versionNumber)
)
}

/**
* 根据 minecraft 协议发送数据包
*
*/
private fun MinecraftOutputStream.sendPacket(packet: ByteArray) {
writeVarInt(packet.size)
write(packet)
}

/**
* 根据 minecraft 协议编码握手包
*
* @param version 服务器版本 默认为:-1
* @param address 服务器地址
* @param port 服务器端口
* @param state 下一步状态 1:状态 2:登录
* @return
*/
private fun encodeHandshake(version: Int = -1, address: String, port: Int, state: Int = 1): ByteArray {
val byte = ByteArrayOutputStream()
val handshake = MinecraftOutputStream(DataOutputStream(byte))
handshake.writeByte(0x00)
handshake.writeVarInt(version)
handshake.writeUTF(address)
handshake.writeShort(port)
handshake.writeVarInt(state)
return byte.toByteArray()
}

/**
* 根据 minecraft 协议解码握手包状态响应
*
* @return 服务器状态 json 字符串
*/
private fun decodeHandshakeResponse(inStream: MinecraftInputStream): String {
val responseLength = inStream.readVarInt()
val packetId = inStream.readVarInt()
if (packetId == -1) throw IOException("Premature end of stream.")
if (packetId != 0x00) throw IOException("Invalid packetID: $packetId")
return inStream.readUTF(responseLength)
}

/**
* 获取 ping 延迟
*
* @param outStream
* @param inStream
* @return 成功返回延迟时间(毫秒),失败返回-1
*/
private fun getPingDelay(inStream: MinecraftInputStream, outStream: MinecraftOutputStream) = try {
// 发送 ping 包
val now = System.currentTimeMillis()
outStream.writeByte(0x09)
outStream.writeByte(0x01)
outStream.writeLong(now)
// 读取 ping 响应
inStream.readVarInt()
val id = inStream.readVarInt()
if (id == -1) throw IOException("Premature end of stream.")
if (id != 0x01) throw IOException("Invalid packetID")
(System.currentTimeMillis() - inStream.readLong()).toInt()
} catch (e: Exception) {
e.printStackTrace()
-1
}
}





78 changes: 78 additions & 0 deletions src/main/kotlin/entity/ServerStatus.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2023 limbang and contributors.
*
* 此源代码的使用受 GNU AGPLv3 许可证的约束,该许可证可在"LICENSE"文件中找到。
* Use of this source code is governed by the GNU AGPLv3 license that can be found in the "LICENSE" file.
*/

package top.limbang.minecraft.entity


/**
* ### 服务器状态信息
*
* - [favicon] 服务器图标
* - [description] 服务器描述
* - [playerInfo] 玩家信息
* - [versionName] 版本名称
* - [versionNumber] 版本号
* - [forgeData] Forge相关信息
*/
data class ServerStatus(
val favicon: String,
val description: String,
val playerInfo: PlayerInfo,
val versionName: String,
val versionNumber: Int,
val forgeData: ForgeData
)

/**
* 玩家信息
*
* @property playerMax 最大玩家数
* @property playerOnline 当前在线玩家数
* @property players 玩家列表
*/
data class PlayerInfo(val playerMax: Int, val playerOnline: Int, val players: List<Player>)

/**
* 玩家
*
* @property name 玩家名称
* @property id 玩家ID
*/
data class Player(val name: String, val id: String)

/**
* 模组信息
*
* @property id 模组ID
* @property version 模组版本
*/
data class Mod(val id: String, val version: String)

/**
* 通道信息
*
* @property name 通道名称
* @property version 通道版本
* @property requiredOnClient 是否在客户端必需
*/
data class Channel(val name: String, val version: String, val requiredOnClient: Boolean)

/**
* Forge相关信息
*
* @property fmlNetworkVersion FML网络版本
* @property truncated 是否被截断
* @property mods 模组列表
* @property channels 通道列表
* @constructor Create empty Forge data
*/
data class ForgeData(
val fmlNetworkVersion: Int,
val truncated: Boolean,
val mods: List<Mod>,
val channels: List<Channel>
)
21 changes: 0 additions & 21 deletions src/main/kotlin/extension/ByteArrayStreamExtension.kt

This file was deleted.

Loading

0 comments on commit 5f08207

Please sign in to comment.