aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Network/HotlineClient.swift
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2023-11-30 23:46:57 -0800
committerDustin Mierau <dustin@mierau.me>2023-11-30 23:46:57 -0800
commit885119acdd27bd53ed3096285c3eca3e2d99ad70 (patch)
tree7dce98c071859684ff2aaa157f090b7cdc941a04 /Hotline/Network/HotlineClient.swift
parent30255476781789df318a571622ab732681696139 (diff)
Bunch of work to and now login, user list, handling replies, etc. works. Or at least the framework is there for them to work.
Diffstat (limited to 'Hotline/Network/HotlineClient.swift')
-rw-r--r--Hotline/Network/HotlineClient.swift179
1 files changed, 125 insertions, 54 deletions
diff --git a/Hotline/Network/HotlineClient.swift b/Hotline/Network/HotlineClient.swift
index 5193fe5..b5092a0 100644
--- a/Hotline/Network/HotlineClient.swift
+++ b/Hotline/Network/HotlineClient.swift
@@ -24,12 +24,14 @@ class HotlineClient : ObservableObject {
@Published var userList: [HotlineUser] = []
@Published var chatMessages: [String] = []
- let userName: String = "bolt"
- let userIconID: UInt32 = 128
-
+ var userName: String = "bolt"
+ var userIconID: UInt16 = 128
+ var serverVersion: UInt16 = 151
var server: HotlineServer?
var connection: NWConnection?
+ private var transactionLog: [UInt32:HotlineTransactionType] = [:]
+
init() {
}
@@ -94,6 +96,10 @@ class HotlineClient : ObservableObject {
return
}
+ print("HotlineClient => \(t.id) \(t.type)")
+
+ self.transactionLog[t.id] = t.type
+
c.send(content: t.encoded(), completion: .contentProcessed { [weak self] (error) in
if disconnectOnError, error != nil {
self?.disconnect()
@@ -110,7 +116,7 @@ class HotlineClient : ObservableObject {
return
}
- print("HotlineClient: waiting for transaction...")
+ print("HotlineClient ⏳")
c.receive(minimumIncompleteLength: HotlineTransaction.headerSize, maximumLength: HotlineTransaction.headerSize) { [weak self] (headerData, context, isComplete, error) in
guard let self = self else {
return
@@ -127,39 +133,40 @@ class HotlineClient : ObservableObject {
return
}
- print("HotlineClient: received \(headerData.count) header bytes")
+// print("HotlineClient: received \(headerData.count) header bytes")
if var transaction = self.parseTransaction(data: headerData) {
// Receive additional data if the transaction has data attached to it.
print("DATA SIZE: \(transaction.dataSize)")
if transaction.dataSize > 0 {
- c.receive(minimumIncompleteLength: Int(transaction.dataSize), maximumLength: Int(transaction.dataSize)) { [weak self] (parameterData, context, isComplete, error) in
+ c.receive(minimumIncompleteLength: Int(transaction.dataSize), maximumLength: Int(transaction.dataSize)) { [weak self] (fieldData, context, isComplete, error) in
guard let self = self else {
return
}
- guard let parameterData = parameterData, !parameterData.isEmpty else {
- print("HotlineClient: transaction parameter data is empty!")
+ guard let fieldData = fieldData, !fieldData.isEmpty else {
+ print("HotlineClient: transaction field data is empty!")
self.disconnect()
return
}
- let parameterCount = parameterData.readUInt16(at: 0)!
+ let fieldCount = fieldData.readUInt16(at: 0)!
- if parameterCount > 0 {
+ if fieldCount > 0 {
var dataCursor = 2
- for _ in 0..<parameterCount {
+ for _ in 0..<fieldCount {
if
- let fieldID = parameterData.readUInt16(at: dataCursor),
- let fieldSize = parameterData.readUInt16(at: dataCursor + 2),
- let fieldData = parameterData.readData(at: dataCursor + 4, length: Int(fieldSize)) {
+ let fieldID = fieldData.readUInt16(at: dataCursor),
+ let fieldSize = fieldData.readUInt16(at: dataCursor + 2),
+ let fieldRemainingData = fieldData.readData(at: dataCursor + 4, length: Int(fieldSize)) {
if let fieldType = HotlineTransactionFieldType(rawValue: fieldID) {
- transaction.parameters.append(HotlineTransactionParameter(type: fieldType, dataSize: fieldSize, data: fieldData))
-// transaction.parameters[fieldType] = HotlineTransactionParameter(type: fieldType, dataSize: fieldSize, data: fieldData)
+
+ transaction.fields.append(HotlineTransactionField(type: fieldType, dataSize: fieldSize, data: fieldRemainingData))
+// transaction.parameters[fieldType] = HotlineTransactionField(type: fieldType, dataSize: fieldSize, data: fieldData)
}
else {
- print("HotlineClient: UNKNOWN PARAM TYPE!", fieldID, fieldSize)
+ print("HotlineClient: UNKNOWN FIELD TYPE!", fieldID, fieldSize)
}
dataCursor += 4 + Int(fieldSize)
@@ -201,10 +208,12 @@ class HotlineClient : ObservableObject {
let transactionSize = data.readUInt32(at: 12),
let dataSize = data.readUInt32(at: 16) {
- print("HotlineClient: Parsing transaction type \(type) with data \(dataSize)")
if let transactionType = HotlineTransactionType(rawValue: type) {
return HotlineTransaction(type: transactionType, flags: flags, isReply: isReply, id: id, errorCode: errorCode, totalSize: transactionSize, dataSize: dataSize)
}
+ else {
+ print("HotlineClient: Unknown type \(type) parsing with \(dataSize) bytes")
+ }
}
return nil
@@ -249,8 +258,11 @@ class HotlineClient : ObservableObject {
return
}
- print("HotlineClient: completed handshake")
- self.sendLogin()
+ print("HotlineClient 🤝")
+ self.sendLogin() { [weak self] in
+ self?.sendSetClientUserInfo()
+ self?.sendGetUserList()
+ }
self.receiveTransaction()
}
})
@@ -262,15 +274,13 @@ class HotlineClient : ObservableObject {
}
var t = HotlineTransaction(type: .login)
- t.setParameterEncodedString(type: .userLogin, val: "")
- t.setParameterEncodedString(type: .userPassword, val: "")
- t.setParameterUInt32(type: .userIconID, val: self.userIconID)
- t.setParameterString(type: .userName, val: self.userName)
- t.setParameterUInt32(type: .versionNumber, val: 151)
+ t.setFieldEncodedString(type: .userLogin, val: "")
+ t.setFieldEncodedString(type: .userPassword, val: "")
+ t.setFieldUInt16(type: .userIconID, val: self.userIconID)
+ t.setFieldString(type: .userName, val: self.userName)
+ t.setFieldUInt32(type: .versionNumber, val: 123)
- print("HotlineClient: logging in...")
self.sendTransaction(t) { [weak self] in
- print("HotlineClient: logged in!")
DispatchQueue.main.async {
self?.connectionStatus = .loggedIn
}
@@ -279,44 +289,100 @@ class HotlineClient : ObservableObject {
}
}
+ func sendSetClientUserInfo(callback: (() -> Void)? = nil) {
+ var t = HotlineTransaction(type: .setClientUserInfo)
+ t.setFieldString(type: .userName, val: self.userName)
+ t.setFieldUInt16(type: .userIconID, val: self.userIconID)
+
+ self.sendTransaction(t, callback: callback)
+ }
+
func sendAgree(callback: (() -> Void)? = nil) {
var t = HotlineTransaction(type: .agreed)
- t.setParameterString(type: .userName, val: self.userName)
- t.setParameterUInt32(type: .userIconID, val: self.userIconID)
- t.setParameterUInt32(type: .options, val: 0)
-
- print("HotlineClient: agreeing")
+ t.setFieldString(type: .userName, val: self.userName)
+ t.setFieldUInt16(type: .userIconID, val: self.userIconID)
+ t.setFieldUInt32(type: .options, val: 0)
self.sendTransaction(t, callback: callback)
}
func sendChat(message: String, callback: (() -> Void)? = nil) {
var t = HotlineTransaction(type: .sendChat)
- t.setParameterString(type: .data, val: message)
-
- print("HotlineClient: sending chat...")
+ t.setFieldString(type: .data, val: message)
self.sendTransaction(t, callback: callback)
}
func sendGetUserList(callback: (() -> Void)? = nil) {
let t = HotlineTransaction(type: .getUserNameList)
- print("HotlineClient: fetching user list...")
self.sendTransaction(t, callback: callback)
}
// MARK: - Incoming
+ private func processReply(_ transaction: HotlineTransaction) {
+ guard transaction.errorCode == 0 else {
+ if let errorParam = transaction.getField(type: .errorText), let errorText = errorParam.getString() {
+ print("HotlineClient 😵 \(transaction.errorCode): \(errorText)")
+ }
+ else {
+ print("HotlineClient 😵 \(transaction.errorCode)")
+ }
+ return
+ }
+
+ guard let repliedTransactionType = self.transactionLog[transaction.id] else {
+ return
+ }
+
+ print("HotlineClient reply in response to \(repliedTransactionType)")
+
+ switch(repliedTransactionType) {
+ case .login:
+ print("GOT REPLY TO LOGIN!")
+
+ if
+ let serverVersionField = transaction.getField(type: .versionNumber),
+ let serverVersion = serverVersionField.getUInt16() {
+ self.serverVersion = serverVersion
+ print("SERVER VERSION: \(serverVersion)")
+ }
+ case .getUserNameList:
+ print("GOT USER LIST")
+ var newUserList: [HotlineUser] = []
+ for u in transaction.getFieldList(type: .userNameWithInfo) {
+ let info = u.getUserInfo()
+ print("USER: \(info)")
+ let user = HotlineUser(id: info.id, iconID: info.iconID, status: info.flags, name: info.userName)
+ newUserList.append(user)
+ }
+ DispatchQueue.main.async {
+ self.userList = newUserList
+ }
+ default:
+ break
+ }
+ }
+
private func processTransaction(_ transaction: HotlineTransaction) {
+ if transaction.type == .reply {
+ print("HotlineClient <= \(transaction.type) to \(transaction.id):")
+ print(transaction)
+ }
+ else {
+ print("HotlineClient <= \(transaction.type)")
+ }
+
switch(transaction.type) {
case .reply:
- print("HotlineClient: GOT REPLY TRANSACTION? \(transaction)")
+ self.processReply(transaction)
+// print("HotlineClient: Received reply transaction: \(transaction)")
+
case .chatMessage:
- print("HotlineClient: CHAT MESSAGE!")
- if
- let chatTextParam = transaction.getParameter(type: .data),
+ if
+ let chatTextParam = transaction.getField(type: .data),
let chatText = chatTextParam.getString(),
- let userNameParam = transaction.getParameter(type: .userName),
+ let userNameParam = transaction.getField(type: .userName),
let userName = userNameParam.getString(),
- let userIDParam = transaction.getParameter(type: .userID),
+ let userIDParam = transaction.getField(type: .userID),
let userID = userIDParam.getUInt16() {
print("HotlineClient: \(userName):\(userID): \(chatText)")
DispatchQueue.main.async {
@@ -325,37 +391,42 @@ class HotlineClient : ObservableObject {
}
case .getUserNameList:
print("HotlineClient: GOT USER NAME LIST!")
- let userList = transaction.getParameterList(type: .userInfo)
+ let userList = transaction.getFieldList(type: .userNameWithInfo)
for u in userList {
let userInfo = u.getUserInfo()
print("HotlineClient: user \(userInfo.userName)")
}
case .notifyOfUserChange:
- print("HotlineClient: user changed")
- if let p = transaction.getParameter(type: .userName),
+// print("HotlineClient: user changed")
+ if let p = transaction.getField(type: .userName),
let userName = p.getString() {
- print("HotlineClient: user name \(userName)")
+ print("HotlineClient: User changed \(userName)")
}
case .disconnectMessage:
- print("HotlineClient: DISCONNECTED BY SERVER!")
+ print("HotlineClient ❌")
self.disconnect()
case .showAgreement:
- if let agreementParam = transaction.getParameter(type: .data) {
+ if let noAgreementField = transaction.getField(type: .noServerAgreement) {
+ print("NO AGREEMENT?")
+ }
+ if let agreementParam = transaction.getField(type: .data) {
if let agreementText = agreementParam.getString() {
- print("AGREEMENT:", agreementText)
+ print("\n\n--------------------------\n")
+ print(agreementText)
+ print("\n--------------------------\n\n")
DispatchQueue.main.async {
self.agreement = agreementText
}
- self.sendAgree() {
+// self.sendAgree() {
// self.sendGetUserList()
- }
+// }
}
}
case .userAccess:
- print("HotlineClient: user access transaction.")
+ print("")
default:
- print("HotlineClient: UNKNOWN transaction \(transaction.type) with \(transaction.parameters.count) parameters")
- print(transaction.parameters)
+ print("HotlineClient: UNKNOWN transaction \(transaction.type) with \(transaction.fields.count) parameters")
+ print(transaction.fields)
}
}
}