From 885119acdd27bd53ed3096285c3eca3e2d99ad70 Mon Sep 17 00:00:00 2001 From: Dustin Mierau Date: Thu, 30 Nov 2023 23:46:57 -0800 Subject: Bunch of work to and now login, user list, handling replies, etc. works. Or at least the framework is there for them to work. --- Hotline/Network/HotlineClient.swift | 179 +++++++++++++++++++++++++----------- 1 file changed, 125 insertions(+), 54 deletions(-) (limited to 'Hotline/Network/HotlineClient.swift') 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.. 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) } } } -- cgit