Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Sources/SwiftScaffolding/Client/ScaffoldingClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public final class ScaffoldingClient {
}

/// 使用指定的 EasyTier 创建连接到指定房间的 `ScaffoldingClient`。
///
/// - Parameters:
/// - easyTier: 使用的 EasyTier。
/// - playerName: 玩家名。
Expand All @@ -52,8 +53,9 @@ public final class ScaffoldingClient {
}

/// 连接到房间。
///
/// 该方法返回后,必须每隔 5s 调用一次 `heartbeat()` 方法。
/// https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#拓展协议
/// [标准联机流程](https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#拓展协议)
/// - Parameters:
/// - roomCode: 房间码。
/// - checkServer: 是否检查联机中心返回的 Minecraft 服务器端口号。
Expand Down Expand Up @@ -120,8 +122,11 @@ public final class ScaffoldingClient {
}

/// 向联机中心发送请求。
///
/// 发送成功后,该方法会一直等待联机中心返回响应。
/// - Parameters:
/// - name: 请求类型。
/// - timeout: 超时时间。
/// - body: 请求体构造函数。
/// - Returns: 联机中心的响应。
@discardableResult
Expand Down
8 changes: 7 additions & 1 deletion Sources/SwiftScaffolding/EasyTier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public final class EasyTier {
private var rpcPort: UInt16?

/// 创建一个 EasyTier 实例。
///
/// - Parameters:
/// - coreURL: `easytier-core` 的路径。
/// - cliURL: `easytier-cli` 的路径。
Expand All @@ -31,6 +32,8 @@ public final class EasyTier {
}

/// 启动 EasyTier。
///
/// 如果已经启动了一个进程,旧进程会被杀死。
/// - Parameters:
/// - args: `easytier-core` 的参数。
/// - terminationHandler: 进程退出回调,不会在正常 `terminate()` 时被调用。
Expand Down Expand Up @@ -83,9 +86,10 @@ public final class EasyTier {
}

/// 以 JSON 模式调用 `easytier-cli`。
/// 如果 `easytier-cli` 报错,会抛出 `EasyTierError.cliError` 错误。
///
/// - Parameter args: `easytier-cli` 的参数。
/// - Returns: 调用结果,不是 JSON 时为 `nil`。
/// - Throws: 如果 `easytier-cli` 报错,会抛出 `EasyTierError.cliError` 错误。
@discardableResult
public func callCLI(_ args: String...) throws -> JSON? {
guard let rpcPort = self.rpcPort else { throw ConnectionError.failedToAllocatePort }
Expand Down Expand Up @@ -144,6 +148,7 @@ public final class EasyTier {

extension EasyTier {
/// 添加端口转发规则。
///
/// - Parameters:
/// - protocol: 使用的协议类型,默认为 `tcp`。
/// - bind: 本地绑定地址。
Expand All @@ -154,6 +159,7 @@ extension EasyTier {
}

/// 移除端口转发规则。
///
/// - Parameters:
/// - protocol: 使用的协议类型,默认为 `tcp`。
/// - bind: 本地绑定地址。
Expand Down
9 changes: 7 additions & 2 deletions Sources/SwiftScaffolding/Scaffolding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public final class Scaffolding {
internal static let networkQueue: DispatchQueue = DispatchQueue(label: "SwiftScaffolding.Network")

/// 根据设备的主板唯一标识符生成设备标识符。
/// https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#machine_id
///
/// [machine_id 文档](https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#machine_id)
/// - Returns: 设备标识符。
public static func getMachineID(forHost: Bool = false) -> String {
let service: io_service_t = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
Expand All @@ -26,7 +27,8 @@ public final class Scaffolding {
}

/// 发送 Scaffolding 请求。
/// https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#联机信息获取协议
///
/// [协议文档](https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#联机信息获取协议)
/// - Parameters:
/// - type: 请求类型。
/// - connection: 到联机中心的连接。
Expand Down Expand Up @@ -94,6 +96,7 @@ public final class Scaffolding {
}

/// 检查本地指定端口是否存在一个 Minecraft 服务器。
///
/// - Parameters:
/// - port: 服务器端口。
public static func checkMinecraftServer(on port: UInt16, timeout: Double = 10) async -> Bool {
Expand All @@ -119,6 +122,7 @@ public final class Scaffolding {
public let data: Data

/// 根据响应状态码与响应体创建响应。
///
/// - Parameters:
/// - status: 响应状态。
/// - data: 响应体。
Expand All @@ -128,6 +132,7 @@ public final class Scaffolding {
}

/// 根据响应状态码与响应体构造函数创建响应。
///
/// - Parameters:
/// - status: 响应状态。
/// - body: 响应体构造函数。
Expand Down
5 changes: 5 additions & 0 deletions Sources/SwiftScaffolding/Server/RequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public class RequestHandler {
}

/// 注册请求处理器。
///
/// `handler` 闭包接受两个参数:
/// - `sender`:一个 `Sender` 对象,包含发送者的档案信息和连接对象。
/// - `buffer`:一个用于读取请求体的 `ByteBuffer`。
///
/// - Parameters:
/// - type: 请求类型,例如 `c:ping`。
/// - handler: 请求处理函数。
Expand Down
13 changes: 10 additions & 3 deletions Sources/SwiftScaffolding/Server/ScaffoldingServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public final class ScaffoldingServer {
easyTier.terminate()
}

/// 使用指定的 EasyTier 创建联机中心。
/// 创建联机中心。
///
/// - Parameters:
/// - easyTier: 使用的 EasyTier。
/// - roomCode: 房间码。若不合法,将在 `createRoom()` 中抛出 `RoomError.invalidRoomCode` 错误。
Expand Down Expand Up @@ -54,8 +55,11 @@ public final class ScaffoldingServer {
self.handler.server = self
}

/// 启动连接监听器。
public func startListener() async throws {
/// 启动联机中心监听器。
///
/// 默认会在 `13452` 端口监听。若该端口被占用,会重新申请一个端口。
/// - Returns: 联机中心端口号。
public func startListener() async throws -> UInt16 {
let port: UInt16 = try ConnectionUtil.getPort(13452)
listener = try NWListener(using: .tcp, on: .init(integerLiteral: port))
listener.newConnectionHandler = { connection in
Expand Down Expand Up @@ -100,9 +104,12 @@ public final class ScaffoldingServer {
listener.start(queue: Scaffolding.networkQueue)
}
Logger.info("ScaffoldingServer listener started at 127.0.0.1:\(port)")
return port
}

/// 创建 EasyTier 网络。
///
/// 如果只是本地测试,无需创建 EasyTier 网络,可以直接使用 `ScaffoldingClient.connectDirectly(port:)` 连接。
/// - Parameter terminationHandler: 进程退出回调,不会在正常关闭时被调用。
public func createRoom(terminationHandler: ((Process) -> Void)? = nil) throws {
guard let listener = listener,
Expand Down
31 changes: 19 additions & 12 deletions Sources/SwiftScaffolding/Util/ByteBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,25 @@ public class ByteBuffer {

// MARK: - 初始化

/// 构造一个 `ByteBuffer` 用于读取数据包内容
/// - Parameter data: 数据包内容
/// 构造一个 `ByteBuffer` 用于读取 `Data`。
public init(data: Data) {
self.data = data
self.index = 0
}

/// 构造一个 `ByteBuffer` 用于写入数据
/// 构造一个 `ByteBuffer` 用于写入数据
public init() {
self.data = Data()
self.index = 0
}

// MARK: - 读取

/// 读取 `n` 个字节的数据
/// - Parameter length: 读取的数据长度
/// - Returns: 长度为 `length` 字节的 `Data`
/// 读取 `n` 个字节的数据。
///
/// - Parameter length: 读取的数据长度。
/// - Returns: 长度为 `length` 字节的 `Data`。
/// - Throws: 若剩余字节不足,抛出 `ConnectionError.notEnoughBytes` 错误。
public func readData(length: any BinaryInteger) throws -> Data {
guard let intLength: Int = .init(exactly: length) else {
throw ConnectionError.notEnoughBytes
Expand All @@ -43,24 +44,27 @@ public class ByteBuffer {
return data.subdata(in: index..<index + intLength)
}

/// 读取一个 `UInt8`
/// - Returns: UInt8
/// 读取一个 `UInt8`。
///
/// - Throws: 若剩余字节不足,抛出 `ConnectionError.notEnoughBytes` 错误。
public func readUInt8() throws -> UInt8 {
let data: Data = try readData(length: 1)
let value: UInt8 = data.withUnsafeBytes { $0.load(as: UInt8.self) }
return value
}

/// 读取一个 `UInt16`(大端序)
/// - Returns: UInt16
/// 读取一个 `UInt16`(大端序)。
///
/// - Throws: 若剩余字节不足,抛出 `ConnectionError.notEnoughBytes` 错误。
public func readUInt16() throws -> UInt16 {
let data: Data = try readData(length: 2)
let value: UInt16 = data.withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
return value
}

/// 读取一个 `UInt32`(大端序)
/// - Returns: UInt32
/// 读取一个 `UInt32`(大端序)。
///
/// - Throws: 若剩余字节不足,抛出 `ConnectionError.notEnoughBytes` 错误。
public func readUInt32() throws -> UInt32 {
let data: Data = try readData(length: 4)
let value: UInt32 = data.withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }
Expand All @@ -70,6 +74,9 @@ public class ByteBuffer {
/// 读取一个 **UTF-8** 字符串。
///
/// - Parameter length: 字符串长度。
/// - Throws:
/// - 若剩余字节不足,抛出 `ConnectionError.notEnoughBytes` 错误。
/// - 若解析字符串失败,抛出 `ConnectionError.failedToDecodeString` 错误。
public func readString(_ length: (any BinaryInteger)? = nil) throws -> String {
let length: any BinaryInteger = length ?? data.count
let data: Data = try readData(length: length)
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftScaffolding/Util/ConnectionUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Network

internal enum ConnectionUtil {
/// 向目标地址创建一个 TCP 连接。
///
/// 该方法会在 `connection.state` 变为 `.ready` 时正常返回。
/// - Parameters:
/// - host: 目标地址。
/// - port: 目标端口。
Expand Down
4 changes: 3 additions & 1 deletion Sources/SwiftScaffolding/Util/RoomCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

import Foundation

/// https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#联机房间码
/// 规范文档:[Scaffolding-MC/Scaffolding-MC](https://github.com/Scaffolding-MC/Scaffolding-MC/blob/main/README.md#联机房间码)
public enum RoomCode {
private static let charset: [Character] = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ".map { $0 }

/// 生成符合 Scaffolding 规范的房间码。
///
/// - Returns: 生成的房间码。
public static func generate() -> String {
let b: Int = 34
Expand Down Expand Up @@ -40,6 +41,7 @@ public enum RoomCode {
}

/// 验证是否是符合 Scaffolding 规范的房间码。
///
/// - Parameter code: 房间码。
/// - Returns: 一个布尔值,为 `true` 时代表该房间码符合 Scaffolding 规范。
public static func isValid(code: String) -> Bool {
Expand Down