Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
13 changes: 4 additions & 9 deletions Sources/SwiftScaffolding/Client/ScaffoldingClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public final class ScaffoldingClient {
try await heartbeat()
try await fetchProtocols()

let serverPort: UInt16 = ByteBuffer(data: try await sendRequest("c:server_port").data).readUInt16()
let serverPort: UInt16 = try await sendRequest("c:server_port").parse(using: { try $0.readUInt16() })
room.serverPort = serverPort
Logger.info("Minecraft server is ready: 127.0.0.1:\(serverPort)")
}
Expand Down Expand Up @@ -178,7 +178,7 @@ public final class ScaffoldingClient {
try await heartbeat()
try await fetchProtocols()

let serverPort: UInt16 = ByteBuffer(data: try await sendRequest("c:server_port").data).readUInt16()
let serverPort: UInt16 = try await sendRequest("c:server_port").parse(using: { try $0.readUInt16() })
let localPort: UInt16 = try ConnectionUtil.getPort(serverPort)
try easyTier.addPortForward(bind: "127.0.0.1:\(localPort)", destination: "\(serverNodeIp!):\(serverPort)")

Expand All @@ -195,14 +195,9 @@ public final class ScaffoldingClient {

private func fetchProtocols() async throws {
let response: Scaffolding.Response = try await sendRequest("c:protocols") { buf in
buf.writeData(protocols.joined(separator: "\0").data(using: .utf8)!)
buf.writeString(protocols.joined(separator: "\0"))
}
guard let rawProtocols: String = response.text else {
Logger.error("Failed to parse c:protocols response")
self.serverProtocols = []
return
}
self.serverProtocols = rawProtocols.split(separator: "\0").map(String.init)
self.serverProtocols = try response.parse(using: { try $0.readString() }).split(separator: "\0").map(String.init)
}

private func assertReady() throws {
Expand Down
14 changes: 14 additions & 0 deletions Sources/SwiftScaffolding/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public enum ConnectionError: LocalizedError, Equatable {
case missingConnection
case invalidConnectionState
case failedToAllocatePort
case noEnoughBytes
case failedToDecodeString

public var errorDescription: String? {
switch self {
Expand Down Expand Up @@ -47,6 +49,18 @@ public enum ConnectionError: LocalizedError, Equatable {
bundle: Bundle.module,
comment: "端口分配失败"
)
case .noEnoughBytes:
return NSLocalizedString(
"ConnectionError.noEnoughBytes",
bundle: Bundle.module,
comment: "ByteBuffer 读取即将越界"
)
case .failedToDecodeString:
return NSLocalizedString(
"ConnectionError.failedToDecodeString",
bundle: Bundle.module,
comment: "解析 UTF-8 字符串失败"
)
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions Sources/SwiftScaffolding/Scaffolding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public final class Scaffolding {
) async throws -> Response {
let buffer: ByteBuffer = ByteBuffer()
buffer.writeUInt8(UInt8(type.count))
buffer.writeData(type.data(using: .utf8)!)
buffer.writeString(type)
let bodyBuffer: ByteBuffer = ByteBuffer()
try body(bodyBuffer)
buffer.writeUInt32(UInt32(bodyBuffer.data.count))
Expand Down Expand Up @@ -79,8 +79,8 @@ public final class Scaffolding {
Task {
do {
let headerBuffer: ByteBuffer = .init(data: try await ConnectionUtil.receiveData(from: connection, length: 5, timeout: timeout))
let status: UInt8 = headerBuffer.readUInt8()
let bodyLength: Int = .init(headerBuffer.readUInt32())
let status: UInt8 = try headerBuffer.readUInt8()
let bodyLength: Int = .init(try headerBuffer.readUInt32())
if bodyLength == 0 {
completion(.success(.init(status: 0, data: .init())))
return
Expand Down Expand Up @@ -117,7 +117,6 @@ public final class Scaffolding {
public final class Response {
public let status: UInt8
public let data: Data
public var text: String? { String(data: data, encoding: .utf8) }

/// 根据响应状态码与响应体创建响应。
/// - Parameters:
Expand All @@ -138,6 +137,10 @@ public final class Scaffolding {
body(buffer)
self.data = buffer.data
}

public func parse<T>(using parser: (ByteBuffer) throws -> T) rethrows -> T {
return try parser(ByteBuffer(data: data))
}
}

private init() {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftScaffolding/Server/RequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class RequestHandler {
responseBuffer.writeUInt8(255)
let message: String = "Unknown request"
responseBuffer.writeUInt32(UInt32(message.count))
responseBuffer.writeData(message.data(using: .utf8)!)
responseBuffer.writeString(message)
return
}

Expand All @@ -67,7 +67,7 @@ public class RequestHandler {

registerHandler(for: "c:protocols") { sender, requestBody in
let protocols: String = Array(self.handlers.keys).joined(separator: "\0")
return .init(status: 0, data: protocols.data(using: .utf8)!)
return .init(status: 0) { $0.writeString(protocols) }
}

registerHandler(for: "c:server_port") { sender, requestBody in
Expand Down
6 changes: 3 additions & 3 deletions Sources/SwiftScaffolding/Server/ScaffoldingServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,11 @@ public final class ScaffoldingServer {
let headerBuffer: ByteBuffer = .init()
headerBuffer.writeData(try await ConnectionUtil.receiveData(from: connection, length: 1))

let typeLength: Int = Int(headerBuffer.readUInt8())
let typeLength: Int = Int(try headerBuffer.readUInt8())
headerBuffer.writeData(try await ConnectionUtil.receiveData(from: connection, length: typeLength + 4))
guard let type = String(data: headerBuffer.readData(length: typeLength), encoding: .utf8) else { return }
let type: String = try headerBuffer.readString(typeLength)

let bodyLength: Int = Int(headerBuffer.readUInt32())
let bodyLength: Int = Int(try headerBuffer.readUInt32())
let bodyData: Data = try await ConnectionUtil.receiveData(from: connection, length: bodyLength)

let responseBuffer: ByteBuffer = .init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,54 @@ public class ByteBuffer {
/// 读取 `n` 个字节的数据
/// - Parameter length: 读取的数据长度
/// - Returns: 长度为 `length` 字节的 `Data`
public func readData(length: Int) -> Data {
if length == 0 { return Data() }
defer { index += length }
return data.subdata(in: index..<index + length)
public func readData(length: any BinaryInteger) throws -> Data {
guard let intLength: Int = .init(exactly: length) else {
throw ConnectionError.noEnoughBytes
}
if intLength == 0 { return Data() }
if index + intLength > data.count {
throw ConnectionError.noEnoughBytes
}
defer { index += intLength }
return data.subdata(in: index..<index + intLength)
}

/// 读取一个 `UInt8`
/// - Returns: UInt8
public func readUInt8() -> UInt8 {
let data: Data = readData(length: 1)
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
public func readUInt16() -> UInt16 {
let data: Data = readData(length: 2)
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
public func readUInt32() -> UInt32 {
let data: Data = readData(length: 4)
public func readUInt32() throws -> UInt32 {
let data: Data = try readData(length: 4)
let value: UInt32 = data.withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }
return value
}

/// 读取一个 **UTF-8** 字符串。
///
/// - Parameter length: 字符串长度。
public func readString(_ length: (any BinaryInteger)? = nil) throws -> String {
let length: any BinaryInteger = length ?? data.count
let data: Data = try readData(length: length)
guard let string: String = .init(data: data, encoding: .utf8) else {
throw ConnectionError.failedToDecodeString
}
return string
}

// MARK: - 写入

/// 写入一段 `Data`
Expand Down Expand Up @@ -92,4 +110,10 @@ public class ByteBuffer {
let d: Data = Data(bytes: &v, count: MemoryLayout<UInt32>.size)
writeData(d)
}

/// 写入一个 **UTF-8** 字符串。
public func writeString(_ str: String) {
let data: Data = str.data(using: .utf8)!
writeData(data)
}
}
2 changes: 2 additions & 0 deletions Sources/SwiftScaffolding/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"ConnectionError.connectionClosed" = "The connection was closed.";
"ConnectionError.connectionIsNotReady" = "The connection is not established, or the connection state is invalid.";
"ConnectionError.failedToAllocatePort" = "Failed to allocate port.";
"ConnectionError.noEnoughBytes" = "The peer did not send enough data.";
"ConnectionError.failedToDecodeString" = "Failed to decode string.";

"RoomError.invalidRoomCode" = "The room code was invalid.";
"RoomError.roomClosed" = "The room has been closed, or the network is unstable.";
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftScaffolding/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"ConnectionError.connectionClosed" = "连接被关闭。";
"ConnectionError.connectionIsNotReady" = "连接未被建立,或者连接状态异常。";
"ConnectionError.failedToAllocatePort" = "端口分配失败。";
"ConnectionError.noEnoughBytes" = "对端未发送足够长的数据。";
"ConnectionError.failedToDecodeString" = "解析字符串失败。";

"RoomError.invalidRoomCode" = "无效的房间码。";
"RoomError.roomClosed" = "房间已被关闭,或者网络不稳定。";
Expand Down