🐛 Fix windows rpc
This commit is contained in:
		| @@ -3,6 +3,7 @@ import 'dart:convert'; | |||||||
| import 'dart:developer' as developer; | import 'dart:developer' as developer; | ||||||
| import 'dart:ffi'; | import 'dart:ffi'; | ||||||
| import 'dart:io'; | import 'dart:io'; | ||||||
|  | import 'dart:isolate'; | ||||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:island/pods/network.dart'; | import 'package:island/pods/network.dart'; | ||||||
| @@ -53,6 +54,7 @@ class ActivityRpcServer { | |||||||
|   HttpServer? _httpServer; |   HttpServer? _httpServer; | ||||||
|   ServerSocket? _ipcServer; |   ServerSocket? _ipcServer; | ||||||
|   int? _pipeHandle; // For Windows named pipe |   int? _pipeHandle; // For Windows named pipe | ||||||
|  |   Timer? _ipcTimer; // Store timer for cancellation | ||||||
|   final List<WebSocketChannel> _wsSockets = []; |   final List<WebSocketChannel> _wsSockets = []; | ||||||
|   final List<_IpcSocketWrapper> _ipcSockets = []; |   final List<_IpcSocketWrapper> _ipcSockets = []; | ||||||
|  |  | ||||||
| @@ -155,13 +157,13 @@ class ActivityRpcServer { | |||||||
|  |  | ||||||
|     // Start WebSocket server |     // Start WebSocket server | ||||||
|     while (port <= portRange[1]) { |     while (port <= portRange[1]) { | ||||||
|       developer.log('trying port $port', name: kRpcLogPrefix); |       developer.log('Trying port $port', name: kRpcLogPrefix); | ||||||
|       try { |       try { | ||||||
|         _httpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, port); |         _httpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, port); | ||||||
|         developer.log('listening on $port', name: kRpcLogPrefix); |         developer.log('Listening on $port', name: kRpcLogPrefix); | ||||||
|  |  | ||||||
|         shelf_io.serveRequests(_httpServer!, (Request request) async { |         shelf_io.serveRequests(_httpServer!, (Request request) async { | ||||||
|           developer.log('new request', name: kRpcLogPrefix); |           developer.log('New request', name: kRpcLogPrefix); | ||||||
|           if (request.headers['upgrade']?.toLowerCase() == 'websocket') { |           if (request.headers['upgrade']?.toLowerCase() == 'websocket') { | ||||||
|             final handler = webSocketHandler((WebSocketChannel channel, _) { |             final handler = webSocketHandler((WebSocketChannel channel, _) { | ||||||
|               _wsSockets.add(channel); |               _wsSockets.add(channel); | ||||||
| @@ -170,7 +172,7 @@ class ActivityRpcServer { | |||||||
|             return handler(request); |             return handler(request); | ||||||
|           } |           } | ||||||
|           developer.log( |           developer.log( | ||||||
|             'new request disposed due to not websocket', |             'New request disposed due to not websocket', | ||||||
|             name: kRpcLogPrefix, |             name: kRpcLogPrefix, | ||||||
|           ); |           ); | ||||||
|           return Response.notFound('Not a WebSocket request'); |           return Response.notFound('Not a WebSocket request'); | ||||||
| @@ -181,9 +183,10 @@ class ActivityRpcServer { | |||||||
|         if (e is SocketException && e.osError?.errorCode == 98) { |         if (e is SocketException && e.osError?.errorCode == 98) { | ||||||
|           developer.log('$port in use!', name: kRpcLogPrefix); |           developer.log('$port in use!', name: kRpcLogPrefix); | ||||||
|         } else { |         } else { | ||||||
|           developer.log('http error: $e', name: kRpcLogPrefix); |           developer.log('HTTP error: $e', name: kRpcLogPrefix); | ||||||
|         } |         } | ||||||
|         port++; |         port++; | ||||||
|  |         await Future.delayed(Duration(milliseconds: 100)); // Add delay | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -244,42 +247,62 @@ class ActivityRpcServer { | |||||||
|  |  | ||||||
|       developer.log('IPC named pipe created at \\\\.\\pipe\\discord-ipc', name: kRpcIpcLogPrefix); |       developer.log('IPC named pipe created at \\\\.\\pipe\\discord-ipc', name: kRpcIpcLogPrefix); | ||||||
|  |  | ||||||
|       // Start listening for connections in a separate isolate or async loop |       // Start listening for connections in a separate isolate | ||||||
|       _listenWindowsIpc(); |       _listenWindowsIpc(); | ||||||
|     } finally { |     } finally { | ||||||
|       free(pipeName); |       free(pipeName); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Listen for Windows IPC connections |   // Listen for Windows IPC connections in an isolate | ||||||
|   void _listenWindowsIpc() { |   void _listenWindowsIpc() async { | ||||||
|     Timer.periodic(Duration(milliseconds: 100), (timer) async { |     final receivePort = ReceivePort(); | ||||||
|       if (_pipeHandle == null || _pipeHandle == INVALID_HANDLE_VALUE) { |     await Isolate.spawn(_windowsIpcIsolate, receivePort.sendPort); | ||||||
|         timer.cancel(); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       final connected = ConnectNamedPipe(_pipeHandle!, nullptr); |     receivePort.listen((message) { | ||||||
|       if (connected != 0 || GetLastError() == ERROR_PIPE_CONNECTED) { |       if (message is int) { | ||||||
|         final socketWrapper = _IpcSocketWrapper(_pipeHandle!); |         final socketWrapper = _IpcSocketWrapper(message); | ||||||
|         _ipcSockets.add(socketWrapper); |         _ipcSockets.add(socketWrapper); | ||||||
|         developer.log('New IPC connection on named pipe', name: kRpcIpcLogPrefix); |         developer.log('New IPC connection on named pipe', name: kRpcIpcLogPrefix); | ||||||
|  |  | ||||||
|         // Handle data reading in a separate async function |  | ||||||
|         _handleWindowsIpcData(socketWrapper); |         _handleWindowsIpcData(socketWrapper); | ||||||
|  |         _startWindowsIpcServer(); // Create new pipe for next connection | ||||||
|         // Create a new pipe for the next connection |  | ||||||
|         await _startWindowsIpcServer(); |  | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   static void _windowsIpcIsolate(SendPort sendPort) { | ||||||
|  |     while (true) { | ||||||
|  |       final pipeHandle = CreateNamedPipe( | ||||||
|  |         r'\\.\pipe\discord-ipc'.toNativeUtf16(), | ||||||
|  |         PIPE_ACCESS_DUPLEX, | ||||||
|  |         PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | ||||||
|  |         PIPE_UNLIMITED_INSTANCES, | ||||||
|  |         4096, | ||||||
|  |         4096, | ||||||
|  |         0, | ||||||
|  |         nullptr, | ||||||
|  |       ); | ||||||
|  |       if (pipeHandle == INVALID_HANDLE_VALUE) { | ||||||
|  |         developer.log('Failed to create named pipe: ${GetLastError()}', name: kRpcIpcLogPrefix); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       final connected = ConnectNamedPipe(pipeHandle, nullptr); | ||||||
|  |       if (connected != 0 || GetLastError() == ERROR_PIPE_CONNECTED) { | ||||||
|  |         sendPort.send(pipeHandle); | ||||||
|  |       } | ||||||
|  |       // Avoid tight loop | ||||||
|  |       sleep(Duration(milliseconds: 100)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Handle Windows IPC data |   // Handle Windows IPC data | ||||||
|   void _handleWindowsIpcData(_IpcSocketWrapper socket) async { |   void _handleWindowsIpcData(_IpcSocketWrapper socket) async { | ||||||
|  |     final startTime = DateTime.now(); | ||||||
|     final buffer = malloc.allocate<Uint8>(4096); |     final buffer = malloc.allocate<Uint8>(4096); | ||||||
|     final bytesRead = malloc.allocate<Uint32>(sizeOf<Uint32>()); |     final bytesRead = malloc.allocate<Uint32>(sizeOf<Uint32>()); | ||||||
|     try { |     try { | ||||||
|       while (socket.pipeHandle != null) { |       while (socket.pipeHandle != null) { | ||||||
|  |         final readStart = DateTime.now(); | ||||||
|         final success = ReadFile( |         final success = ReadFile( | ||||||
|           socket.pipeHandle!, |           socket.pipeHandle!, | ||||||
|           buffer.cast(), |           buffer.cast(), | ||||||
| @@ -287,6 +310,8 @@ class ActivityRpcServer { | |||||||
|           bytesRead, |           bytesRead, | ||||||
|           nullptr, |           nullptr, | ||||||
|         ); |         ); | ||||||
|  |         final readDuration = DateTime.now().difference(readStart).inMicroseconds; | ||||||
|  |         developer.log('ReadFile took $readDuration microseconds', name: kRpcIpcLogPrefix); | ||||||
|  |  | ||||||
|         if (success == FALSE && GetLastError() != ERROR_MORE_DATA) { |         if (success == FALSE && GetLastError() != ERROR_MORE_DATA) { | ||||||
|           developer.log('IPC read error: ${GetLastError()}', name: kRpcIpcLogPrefix); |           developer.log('IPC read error: ${GetLastError()}', name: kRpcIpcLogPrefix); | ||||||
| @@ -307,6 +332,8 @@ class ActivityRpcServer { | |||||||
|     } finally { |     } finally { | ||||||
|       malloc.free(buffer); |       malloc.free(buffer); | ||||||
|       malloc.free(bytesRead); |       malloc.free(bytesRead); | ||||||
|  |       final totalDuration = DateTime.now().difference(startTime).inMicroseconds; | ||||||
|  |       developer.log('handleWindowsIpcData took $totalDuration microseconds', name: kRpcIpcLogPrefix); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -314,23 +341,36 @@ class ActivityRpcServer { | |||||||
|   Future<void> stop() async { |   Future<void> stop() async { | ||||||
|     // Stop WebSocket server |     // Stop WebSocket server | ||||||
|     for (var socket in _wsSockets) { |     for (var socket in _wsSockets) { | ||||||
|       await socket.sink.close(); |       try { | ||||||
|  |         await socket.sink.close(); | ||||||
|  |       } catch (e) { | ||||||
|  |         developer.log('Error closing WebSocket: $e', name: kRpcLogPrefix); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     _wsSockets.clear(); |     _wsSockets.clear(); | ||||||
|     await _httpServer?.close(); |     await _httpServer?.close(force: true); | ||||||
|  |  | ||||||
|     // Stop IPC server |     // Stop IPC server | ||||||
|     for (var socket in _ipcSockets) { |     for (var socket in _ipcSockets) { | ||||||
|       socket.close(); |       try { | ||||||
|  |         socket.close(); | ||||||
|  |       } catch (e) { | ||||||
|  |         developer.log('Error closing IPC socket: $e', name: kRpcIpcLogPrefix); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     _ipcSockets.clear(); |     _ipcSockets.clear(); | ||||||
|     if (Platform.isWindows && _pipeHandle != null) { |     if (Platform.isWindows && _pipeHandle != null) { | ||||||
|       CloseHandle(_pipeHandle!); |       try { | ||||||
|  |         CloseHandle(_pipeHandle!); | ||||||
|  |       } catch (e) { | ||||||
|  |         developer.log('Error closing named pipe: $e', name: kRpcIpcLogPrefix); | ||||||
|  |       } | ||||||
|       _pipeHandle = null; |       _pipeHandle = null; | ||||||
|     } |     } | ||||||
|  |     _ipcTimer?.cancel(); | ||||||
|     await _ipcServer?.close(); |     await _ipcServer?.close(); | ||||||
|  |  | ||||||
|     developer.log('servers stopped', name: kRpcLogPrefix); |     developer.log('Servers stopped', name: kRpcLogPrefix); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Handle new WebSocket connection |   // Handle new WebSocket connection | ||||||
| @@ -343,7 +383,7 @@ class ActivityRpcServer { | |||||||
|     final origin = request.headers['origin'] ?? ''; |     final origin = request.headers['origin'] ?? ''; | ||||||
|  |  | ||||||
|     developer.log( |     developer.log( | ||||||
|       'new WS connection! origin: $origin, params: $params', |       'New WS connection! origin: $origin, params: $params', | ||||||
|       name: kRpcLogPrefix, |       name: kRpcLogPrefix, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -353,14 +393,14 @@ class ActivityRpcServer { | |||||||
|           'https://ptb.discord.com', |           'https://ptb.discord.com', | ||||||
|           'https://canary.discord.com', |           'https://canary.discord.com', | ||||||
|         ].contains(origin)) { |         ].contains(origin)) { | ||||||
|       developer.log('disallowed origin: $origin', name: kRpcLogPrefix); |       developer.log('Disallowed origin: $origin', name: kRpcLogPrefix); | ||||||
|       socket.sink.close(); |       socket.sink.close(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (encoding != 'json') { |     if (encoding != 'json') { | ||||||
|       developer.log( |       developer.log( | ||||||
|         'unsupported encoding requested: $encoding', |         'Unsupported encoding requested: $encoding', | ||||||
|         name: kRpcLogPrefix, |         name: kRpcLogPrefix, | ||||||
|       ); |       ); | ||||||
|       socket.sink.close(); |       socket.sink.close(); | ||||||
| @@ -368,7 +408,7 @@ class ActivityRpcServer { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ver != 1) { |     if (ver != 1) { | ||||||
|       developer.log('unsupported version requested: $ver', name: kRpcLogPrefix); |       developer.log('Unsupported version requested: $ver', name: kRpcLogPrefix); | ||||||
|       socket.sink.close(); |       socket.sink.close(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -392,7 +432,7 @@ class ActivityRpcServer { | |||||||
|  |  | ||||||
|   // Handle new IPC connection |   // Handle new IPC connection | ||||||
|   void _onIpcConnection(Socket socket) { |   void _onIpcConnection(Socket socket) { | ||||||
|     developer.log('new IPC connection!', name: kRpcIpcLogPrefix); |     developer.log('New IPC connection!', name: kRpcIpcLogPrefix); | ||||||
|  |  | ||||||
|     final socketWrapper = _IpcSocketWrapper.fromSocket(socket); |     final socketWrapper = _IpcSocketWrapper.fromSocket(socket); | ||||||
|     _ipcSockets.add(socketWrapper); |     _ipcSockets.add(socketWrapper); | ||||||
| @@ -412,9 +452,17 @@ class ActivityRpcServer { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Handle incoming WebSocket message |   // Handle incoming WebSocket message | ||||||
|   void _onWsMessage(_WsSocketWrapper socket, dynamic data) { |   Future<void> _onWsMessage(_WsSocketWrapper socket, dynamic data) async { | ||||||
|  |     if (data is! String) { | ||||||
|  |       developer.log('Invalid WebSocket message: not a string', name: kRpcLogPrefix); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|     try { |     try { | ||||||
|       final jsonData = jsonDecode(data as String); |       final jsonData = await compute(jsonDecode, data); | ||||||
|  |       if (jsonData is! Map<String, dynamic>) { | ||||||
|  |         developer.log('Invalid WebSocket message: not a JSON object', name: kRpcLogPrefix); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|       developer.log('WS message: $jsonData', name: kRpcLogPrefix); |       developer.log('WS message: $jsonData', name: kRpcLogPrefix); | ||||||
|       handlers['message']?.call(socket, jsonData); |       handlers['message']?.call(socket, jsonData); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user