Netrek passed app review and is now on the MacOS App Store (screenshot at bottom).  This is a free app.
If you ran an alpha release, I suggest deleting it before downloading from the App store.  Otherwise the app store will install the 1.0 release in your alpha release location, as opposed to /Applications.

App store version 1.0 adds full controls in the strategic map window, hints on the welcome screen, a medium-sized help/documentation/credits book, convenient “mayday” and “escort me” buttons, and a tip jar.

I’d appreciate people downloading it and (if you like it) positive reviews in the app store.  I’m hoping that will attract new players.

As promised, I’ve made the source code publicly available at
I chose the MIT license, which is functionally similar to “copyright.h” and “copyright2.h”, but is a “standard license”.  I welcome pull requests (but will review them of course).

I’m still having issues with the “preferred team”.  I thank Stas for his data and debugging suggestions but team selection is not working as expected.  After a “Netrek development break” in April I will come back and figure that out.  I’ll also work on “position extrapolation” to increase frame rate after my break.

Netrek requires MacOS 10.14 Mojave.  Sven asked:
> What's the reason for the Mojave OS requirement?

Rather than the classic BSD sockets API (socket/connect/bind/listen/accept), my Netrek client uses the new Network framework API from Apple.  Source code at   The only problem with this API is it requires Mojave.

It’s a real cool API, and helps prevent programmer mistakes from people like me.  Excerpts below.  I believe it gives me “free” IPv6 support if the hostname returns an AAAA address (I haven’t checked to see if the netrek vanilla server supports IPv6—I don’t see any netrek servers with AAAA DNS records).  FYI it looks like IPv4 is supposed to be deprecated on 2/1/2030. #ipv4flagday

// TcpReader partial excerpts only see
class TcpReader {
     init?(hostname: String, port: Int, delegate: NetworkDelegate) {
        let serverEndpoint = NWEndpoint.Host(hostname)
        guard let portEndpoint = NWEndpoint.Port(rawValue: port) else { return nil }
        connection = NWConnection(host: serverEndpoint, port: portEndpoint, using: .tcp)
        connection.stateUpdateHandler = { [weak self] (newState) in
            switch newState {
            case .ready:
                debugPrint("TcpReader.ready to send")
            case .failed(let error):
                debugPrint("TcpReader.client failed with error \(error)")
            case .setup:
            case .waiting(_):
            case .preparing:
            case .cancelled:
        connection.start(queue: queue)
// the receive function waits asynchronously for new data and then uses the swift “closure” to process the data on a separate thread whenever it arrives.  No select()!  No testing for available data.
    func receive() {
        connection.receive(minimumIncompleteLength: 1, maximumLength: 16384) { (content, context, isComplete, error) in
            if let content = content {
                //debugPrint("content startIndex \(content.startIndex) endIndex \(content.endIndex)" )
                self.delegate.gotData(data: content, from: self.hostname, port: self.port)
    func send(content: Data) {
        connection.send(content: content, completion: .contentProcessed({ (error) in
            if let error = error {
                print("send error: \(error)")
And in the packet analyzer code after processing gotData:


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Screen Shot 2019-04-04 at 9.43.13 AM.png
Type: image/png
Size: 377589 bytes
Desc: not available
URL: <>