├── lib ├── src │ ├── master_query.dart │ ├── exceptions │ │ ├── exceptions.dart │ │ ├── query_exception.dart │ │ └── rcon_exception.dart │ ├── query │ │ ├── source │ │ │ ├── models │ │ │ │ ├── server_rule.dart │ │ │ │ ├── query_player.dart │ │ │ │ ├── server_vac.dart │ │ │ │ ├── server_visibility.dart │ │ │ │ ├── server_os.dart │ │ │ │ ├── server_type.dart │ │ │ │ ├── server_info.dart │ │ │ │ ├── query_player.freezed.dart │ │ │ │ └── server_info.freezed.dart │ │ │ ├── query_packet.dart │ │ │ └── query_socket.dart │ │ └── minecraft │ │ │ ├── models │ │ │ ├── server_info.dart │ │ │ ├── server_ping_info.g.dart │ │ │ ├── server_ping_info.dart │ │ │ ├── server_info.freezed.dart │ │ │ └── server_ping_info.freezed.dart │ │ │ ├── query_packet.dart │ │ │ ├── list_ping_socket.dart │ │ │ └── query_socket.dart │ ├── rcon │ │ ├── rcon_packet.dart │ │ └── rcon_socket.dart │ ├── source_server.dart │ ├── buffer.dart │ └── minecraft_server.dart ├── minecraft_server.dart └── source_server.dart ├── analysis_options.yaml ├── example └── source_server_example.dart ├── pubspec.yaml ├── .gitignore ├── test ├── source_server_test.dart ├── dummy_server_test.dart └── dummy_server.dart ├── README.md ├── CHANGELOG.md └── LICENSE /lib/src/master_query.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/src/exceptions/exceptions.dart: -------------------------------------------------------------------------------- 1 | export 'query_exception.dart'; 2 | export 'rcon_exception.dart'; 3 | -------------------------------------------------------------------------------- /lib/src/exceptions/query_exception.dart: -------------------------------------------------------------------------------- 1 | import '../../source_server.dart'; 2 | 3 | /// Exceptions thrown when the [QuerySocket] fails. 4 | class QueryException implements Exception { 5 | /// Exception description. 6 | final String message; 7 | 8 | /// Initialize an instance of [QueryException] 9 | const QueryException(this.message); 10 | 11 | @override 12 | String toString() => 'QueryException: $message'; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_rule.dart: -------------------------------------------------------------------------------- 1 | import '../../../source_server.dart'; 2 | 3 | /// A rule returned by [SourceServer.getRules]. 4 | class ServerRule { 5 | /// Rule name. 6 | final String name; 7 | 8 | /// Rule value. 9 | final String value; 10 | 11 | /// Construct a new [ServerRule] from its [name] and [value]. 12 | const ServerRule(this.name, this.value); 13 | 14 | @override 15 | String toString() => 'ServerRule($name, $value)'; 16 | } 17 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lint/analysis_options_package.yaml 2 | 3 | analyzer: 4 | strong-mode: 5 | implicit-casts: false 6 | implicit-dynamic: false 7 | 8 | exclude: 9 | - '**/*.g.dart' 10 | - '**/*.freezed.dart' 11 | 12 | linter: 13 | rules: 14 | always_use_package_imports: false 15 | prefer_relative_imports: true 16 | avoid_relative_lib_imports: false 17 | unawaited_futures: true 18 | public_member_api_docs: true 19 | -------------------------------------------------------------------------------- /lib/minecraft_server.dart: -------------------------------------------------------------------------------- 1 | /// Query and RCON implementation of Minecraft servers. 2 | library source_server; 3 | 4 | export 'src/exceptions/exceptions.dart'; 5 | export 'src/minecraft_server.dart'; 6 | export 'src/query/minecraft/list_ping_socket.dart'; 7 | export 'src/query/minecraft/models/server_info.dart'; 8 | export 'src/query/minecraft/models/server_ping_info.dart'; 9 | export 'src/query/minecraft/query_socket.dart'; 10 | export 'src/rcon/rcon_socket.dart'; 11 | export 'src/source_server.dart'; 12 | -------------------------------------------------------------------------------- /example/source_server_example.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | import 'package:source_server/source_server.dart'; 4 | 5 | Future main() async { 6 | final server = await SourceServer.connect('127.0.0.1', 27015); 7 | 8 | final players = await server.getPlayers(); 9 | print(players); 10 | 11 | final rules = await server 12 | .getRules(); 13 | print(rules); 14 | 15 | final status = await server.command('status'); 16 | print(status); 17 | 18 | server.close(); 19 | } 20 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: source_server 2 | description: A dart implementation of Source's Query Protocol and RCON. Supports most of the Source Games + Minecraft. 3 | version: 4.0.0+1 4 | homepage: https://github.com/Hexer10/source_server 5 | 6 | environment: 7 | sdk: '>=2.17.0 <3.0.0' 8 | 9 | dependencies: 10 | collection: ^1.16.0 11 | concurrent_queue: ^1.4.0 12 | freezed_annotation: ^2.0.3 13 | json_annotation: ^4.5.0 14 | logging: ^1.0.2 15 | 16 | dev_dependencies: 17 | build_runner: ^2.1.10 18 | freezed: ^2.0.3+1 19 | json_serializable: ^6.2.0 20 | lint: ^1.8.2 21 | test: ^1.21.1 22 | -------------------------------------------------------------------------------- /lib/source_server.dart: -------------------------------------------------------------------------------- 1 | /// Query and RCON implementation of any Source server. (GoldSRC not supported). 2 | library source_server; 3 | 4 | export 'src/exceptions/exceptions.dart'; 5 | export 'src/query/source/models/query_player.dart'; 6 | export 'src/query/source/models/server_info.dart'; 7 | export 'src/query/source/models/server_os.dart'; 8 | export 'src/query/source/models/server_rule.dart'; 9 | export 'src/query/source/models/server_type.dart'; 10 | export 'src/query/source/models/server_vac.dart'; 11 | export 'src/query/source/models/server_visibility.dart'; 12 | export 'src/query/source/query_socket.dart'; 13 | export 'src/rcon/rcon_socket.dart'; 14 | export 'src/source_server.dart'; 15 | -------------------------------------------------------------------------------- /lib/src/query/source/models/query_player.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'query_player.freezed.dart'; 4 | 5 | @freezed 6 | 7 | /// A player info returned from the query protocol. 8 | class QueryPlayer with _$QueryPlayer { 9 | /// Initialize an instance of [QueryPlayer]. 10 | factory QueryPlayer({ 11 | /// Index of player. 12 | required int index, 13 | 14 | /// Name of the player. 15 | required String name, 16 | 17 | /// Player's score (usually "frags" or "kills".) 18 | required int score, 19 | 20 | /// Time (in seconds) player has been connected to the server. 21 | required double duration, 22 | }) = _QueryPlayer; 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | pubspec.lock 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json -------------------------------------------------------------------------------- /lib/src/query/minecraft/models/server_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'server_info.freezed.dart'; 4 | 5 | @freezed 6 | 7 | /// Minecraft Server Info. 8 | class ServerInfo with _$ServerInfo { 9 | /// Initialize an instance of [ServerInfo]. 10 | factory ServerInfo({ 11 | /// MOTD. 12 | required String motd, 13 | 14 | /// Gametype. 15 | required String gametype, 16 | 17 | /// Map. 18 | required String map, 19 | 20 | /// Number of players on the server. 21 | required int players, 22 | 23 | /// Maximum number of players the server reports it can hold. 24 | required int maxPlayers, 25 | 26 | /// Server's port. 27 | required int port, 28 | 29 | /// Server's name. 30 | required String ip, 31 | }) = _ServerInfo; 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/exceptions/rcon_exception.dart: -------------------------------------------------------------------------------- 1 | import '../../source_server.dart'; 2 | 3 | /// Exceptions thrown when the [RconSocket] fails. 4 | class RconException implements Exception { 5 | /// Exception description. 6 | final String message; 7 | 8 | /// Initialize an instance of [QueryException]. 9 | const RconException(this.message); 10 | 11 | @override 12 | String toString() => 'RconException: $message'; 13 | } 14 | 15 | /// Exceptions thrown when the authentication with the [RconSocket] fails. 16 | class RconAuthenticationException implements RconException { 17 | @override 18 | final String message; 19 | 20 | /// Initialize an instance of [RconAuthenticationException]. 21 | const RconAuthenticationException(this.message); 22 | 23 | @override 24 | String toString() => 'RconAuthenticationException: $message'; 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_vac.dart: -------------------------------------------------------------------------------- 1 | /// Wrapper for the server VAC status. 2 | class ServerVAC { 3 | /// VAC Disabled 4 | static const ServerVAC unsecured = ServerVAC._(0); 5 | 6 | /// Secure VAC server 7 | static const ServerVAC secured = ServerVAC._(1); 8 | 9 | /// The [ServerVAC] as raw int. 10 | final int code; 11 | 12 | /// Initialize an instance of [ServerVAC] from a [code]. 13 | factory ServerVAC(int code) { 14 | if (code == 0) { 15 | return unsecured; 16 | } 17 | if (code == 1) { 18 | return secured; 19 | } 20 | throw ArgumentError.value(code, 'code', 'Invalid server vac'); 21 | } 22 | 23 | const ServerVAC._(this.code); 24 | 25 | @override 26 | String toString() { 27 | switch (this) { 28 | case unsecured: 29 | return 'ServerVAC(unsecured)'; 30 | case secured: 31 | return 'ServerVAC(secured)'; 32 | default: 33 | // Never reaches here. 34 | throw StateError(''); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_visibility.dart: -------------------------------------------------------------------------------- 1 | /// Wrapper for the server visibility. 2 | class ServerVisibility { 3 | /// Public - Not password protected. 4 | static const ServerVisibility public = ServerVisibility._(0); 5 | 6 | /// Private - Password protected. 7 | static const ServerVisibility private = ServerVisibility._(1); 8 | 9 | /// The [ServerVisibility] as raw int. 10 | final int code; 11 | 12 | /// Initialize an instance of [ServerVisibility] from a [code]. 13 | factory ServerVisibility(int code) { 14 | if (code == 0) { 15 | return public; 16 | } 17 | if (code == 1) { 18 | return private; 19 | } 20 | throw ArgumentError.value(code, 'code', 'Invalid server visibility'); 21 | } 22 | 23 | const ServerVisibility._(this.code); 24 | 25 | @override 26 | String toString() { 27 | switch (this) { 28 | case public: 29 | return 'ServerVisibility(public)'; 30 | case private: 31 | return 'ServerVisibility(private)'; 32 | default: 33 | // Never reaches here. 34 | throw StateError(''); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/source_server_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:source_server/source_server.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | Future main() async { 5 | // Make sure to run any source server on your local machine 6 | // and put correct ip, port and rcon password 7 | const svIp = '127.0.0.1'; 8 | const svPort = 27015; 9 | const String? svRconPass = null; 10 | 11 | final server = await SourceServer.connect(svIp, svPort, password: svRconPass); 12 | 13 | test('Get Server Info', () async { 14 | final info = await server.getInfo(); 15 | expect(info, isNotNull); 16 | }); 17 | 18 | test('Get Server Players', () async { 19 | final players = await server.getPlayers(); 20 | expect(players, isA>()); 21 | }); 22 | 23 | test('Get Server Rules', () async { 24 | final rules = await server.getRules(); 25 | expect(rules, isNotNull); 26 | expect(rules, isA>()); 27 | }); 28 | 29 | test( 30 | 'Test Custom Command', 31 | () async { 32 | final status = await server.command('status'); 33 | expect(status, isA()); 34 | }, 35 | skip: svRconPass == null, 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_os.dart: -------------------------------------------------------------------------------- 1 | /// Wrapper for the server OS. 2 | class ServerOS { 3 | /// Linux 4 | static const ServerOS linux = ServerOS._(0x6c); 5 | 6 | /// Windows 7 | static const ServerOS windows = ServerOS._(0x77); 8 | 9 | /// Mac 10 | static const ServerOS mac = ServerOS._(0x6d); 11 | 12 | /// The [ServerOS] as raw int. 13 | final int code; 14 | 15 | /// Initialize an instance of [ServerOS] from a [code]. 16 | factory ServerOS(int code) { 17 | if (code == 0x6c) { 18 | return linux; 19 | } 20 | if (code == 0x77) { 21 | return windows; 22 | } 23 | if (code == 0x6d || code == 0x6f) { 24 | return mac; 25 | } 26 | throw ArgumentError.value(code, 'code', 'Invalid server os'); 27 | } 28 | 29 | const ServerOS._(this.code); 30 | 31 | @override 32 | String toString() { 33 | switch (this) { 34 | case linux: 35 | return 'ServerOS(linux)'; 36 | case windows: 37 | return 'ServerOS(windows)'; 38 | case mac: 39 | return 'ServerOS(mac)'; 40 | default: 41 | // Never reaches here. 42 | throw StateError(''); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/query_packet.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | 3 | /// 4 | class QueryPacket { 5 | /// 6 | static const List magic = [0xfe, 0xfd]; 7 | 8 | /// 9 | static const QueryPacket challenge = 10 | QueryPacket([...magic, 0x09, 0x00, 0x00, 0x00, 0x01]); 11 | 12 | /// 13 | final List bytes; 14 | 15 | /// 16 | const QueryPacket(this.bytes); 17 | 18 | /// 19 | QueryPacket.info(List challenge) 20 | : bytes = [...magic, 0x00, 0x00, 0x00, 0x00, 0x01, ...challenge]; 21 | 22 | /// 23 | QueryPacket.fullInfo(List challenge) 24 | : bytes = [ 25 | ...magic, 26 | 0x00, 27 | 0x00, 28 | 0x00, 29 | 0x00, 30 | 0x01, 31 | ...challenge, 32 | 0x00, 33 | 0x00, 34 | 0x00, 35 | 0x00 36 | ]; 37 | 38 | @override 39 | bool operator ==(Object other) { 40 | if (identical(this, other)) { 41 | return true; 42 | } 43 | if (other is QueryPacket) { 44 | return const IterableEquality().equals(bytes, other.bytes); 45 | } 46 | return false; 47 | } 48 | 49 | @override 50 | int get hashCode => bytes.hashCode; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_type.dart: -------------------------------------------------------------------------------- 1 | /// Wrapper for server types. 2 | class ServerType { 3 | /// Dedicated server 4 | static const ServerType dedicated = ServerType._(0x64); 5 | 6 | /// Non-dedicated server 7 | static const ServerType nonDedicated = ServerType._(0x6c); 8 | 9 | /// SourceTV relay (proxy) 10 | static const ServerType sourceTv = ServerType._(0x70); 11 | 12 | /// The [ServerType] as raw int. 13 | final int code; 14 | 15 | /// Initialize an instance of [ServerType] from a [code]. 16 | factory ServerType(int code) { 17 | if (code == 0x64) { 18 | return dedicated; 19 | } 20 | if (code == 0x6c) { 21 | return nonDedicated; 22 | } 23 | if (code == 0x70) { 24 | return sourceTv; 25 | } 26 | throw ArgumentError.value(code, 'code', 'Invalid server type'); 27 | } 28 | 29 | const ServerType._(this.code); 30 | 31 | @override 32 | String toString() { 33 | switch (this) { 34 | case dedicated: 35 | return 'ServerType(dedicated)'; 36 | case nonDedicated: 37 | return 'ServerType(nonDedicated)'; 38 | case sourceTv: 39 | return 'ServerType(sourceTv)'; 40 | default: 41 | // Never reaches here. 42 | throw StateError(''); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SourceServer 2 | 3 | A library to connect to Source Dedicated Servers [CS:GO, CSS, TF2, ...] 4 | 5 | ## Get started 6 | 7 | Import the library (make sure to check for the latest version). 8 | 9 | ```yaml 10 | depedencies: 11 | source_server: ^3.1.2-dev 12 | ``` 13 | 14 | Connect to the server 15 | 16 | ```dart 17 | final server = await SourceServer.connect('127.0.0.1', 27015, password: '1234'); 18 | ``` 19 | 20 | The password parameter can be supplied to connect via RCON as well. 21 | 22 | After connecting you can issue the query commands: 23 | 24 | ```dart 25 | final info = await server.getInfo(); 26 | final players = await server.getPlayers(); 27 | final rules = await server.getRules(); 28 | ``` 29 | 30 | ## Sending RCON commands 31 | 32 | The RCON password must be supplied to send RCON commands. 33 | 34 | ```dart 35 | final reply = await server.command('status'); 36 | ``` 37 | 38 | the method `command` will return a `Future` string holding the server reply. 39 | 40 | ## Contributing 41 | 42 | - Clone the project. 43 | - Setup a source dedicated server on your local machine 44 | - Install debendencies from `pub` 45 | - Update server details in `test/source_server_test.dart` 46 | - And run `dart test -r expanded` 47 | - For a new feature start by writing a test 48 | 49 | - Describe the test as what you expect. 50 | - Compare the result. 51 | 52 | - When you feel the feature or fix is ready, submit a PR. 53 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 4.0.0+1 2 | - Make `requestPing` true by default. 3 | 4 | # 4.0.0 5 | 6 | - Implemented `MinecraftServer.isConnected` 7 | - Now `MinecraftServer.getPingInfo` takes a named parameter (`requestPing`) which can be used to retrieve the server's 8 | ping from the `ServerPingInfo` class. 9 | - Updated to `v4.0.0` to fix versioning. 10 | 11 | # 3.0.0 12 | 13 | - Updated to dart 2.17.0. 14 | - Code cleanup. 15 | - Minor fixes. 16 | 17 | ## 3.3.0-dev 18 | 19 | - Increased minimum sdk version: 2.14 20 | - Implemented minecraft ping protocol. 21 | - Implemented the new "A2S_PING" challenge: https://steamcommunity.com/discussions/forum/14/2974028351344359625/ 22 | - Improved tests. 23 | - 24 | 25 | ## 3.2.0-dev 26 | 27 | - BREAKING CHANGE: Now `getRules` returns `List`. 28 | - Implemented a dummy server that replicates locally a CS:GO server query and rcon protocol. 29 | - Implement more tests. 30 | 31 | ## 3.1.3-dev 32 | 33 | - The query client now uses a random port (previously it would always listen to the port 6000). Thanks to @mohitkyadav 34 | - Added test. 35 | 36 | ## 3.1.2-dev 37 | 38 | - Implement timeout 39 | - Fix Endianness error 40 | - Overridden `toString` in ServerVisibility, ServerVAC and ServerType classes 41 | 42 | ## 3.1.1-dev 43 | 44 | - Custom exceptions 45 | - Stricter lints 46 | 47 | ## 3.1.0-dev 48 | 49 | - Null-safety 50 | - Bug fixes 51 | 52 | ## 1.0.0 53 | 54 | - Initial version, created by Stagehand 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Mattia (aka Hexah/Hexer10). 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /lib/src/query/source/query_packet.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:collection/collection.dart'; 4 | 5 | /// 6 | class QueryPacket { 7 | /// 8 | static const QueryPacket info = QueryPacket([ 9 | 0xff, 10 | 0xff, 11 | 0xff, 12 | 0xff, 13 | 0x54, // T 14 | 0x53, // Source Engine Query 15 | 0x6f, 16 | 0x75, 17 | 0x72, 18 | 0x63, 19 | 0x65, 20 | 0x20, 21 | 0x45, 22 | 0x6e, 23 | 0x67, 24 | 0x69, 25 | 0x6e, 26 | 0x65, 27 | 0x20, 28 | 0x51, 29 | 0x75, 30 | 0x65, 31 | 0x72, 32 | 0x79, 33 | 0x00 34 | ]); 35 | 36 | /// 37 | static const QueryPacket challenge = 38 | QueryPacket([0xff, 0xff, 0xff, 0xff, 0x56, 0xff, 0xff, 0xff, 0xff]); 39 | 40 | /// 41 | final List bytes; 42 | 43 | /// 44 | const QueryPacket(this.bytes); 45 | 46 | /// 47 | QueryPacket.players(Uint8List challenge) 48 | : bytes = [0xff, 0xff, 0xff, 0xff, 0x55, ...challenge]; 49 | 50 | /// 51 | QueryPacket.rules(Uint8List challenge) 52 | : bytes = [0xff, 0xff, 0xff, 0xff, 0x56, ...challenge]; 53 | 54 | /// 55 | QueryPacket.infoChallenge(Uint8List challenge) 56 | : bytes = [...info.bytes, ...challenge]; 57 | 58 | @override 59 | bool operator ==(Object other) { 60 | if (identical(this, other)) { 61 | return true; 62 | } 63 | if (other is QueryPacket) { 64 | return const IterableEquality().equals(bytes, other.bytes); 65 | } 66 | return false; 67 | } 68 | 69 | @override 70 | int get hashCode => bytes.hashCode; 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'server_os.dart'; 4 | import 'server_type.dart'; 5 | import 'server_vac.dart'; 6 | import 'server_visibility.dart'; 7 | 8 | part 'server_info.freezed.dart'; 9 | 10 | @freezed 11 | 12 | /// Info returned from a A2A_INFO packet. 13 | class ServerInfo with _$ServerInfo { 14 | /// Initialize an instance of [ServerInfo]. 15 | factory ServerInfo({ 16 | /// Protocol version used by the server. 17 | required int protocol, 18 | 19 | /// Name of the server. 20 | required String name, 21 | 22 | /// Map the server has currently loaded. 23 | required String map, 24 | 25 | /// Name of the folder containing the game files. 26 | required String folder, 27 | 28 | /// Full name of the game. 29 | required String game, 30 | 31 | /// Steam Application ID of game. 32 | required int id, 33 | 34 | /// Number of players on the server. 35 | required int players, 36 | 37 | /// Maximum number of players the server reports it can hold. 38 | required int maxPlayers, 39 | 40 | /// Number of bots on the server. 41 | required int bots, 42 | 43 | /// Server type. 44 | required ServerType type, 45 | 46 | /// Server operating system. 47 | required ServerOS os, 48 | 49 | /// Server visibility. Indicates if the server is password protected. 50 | required ServerVisibility visibility, 51 | 52 | /// Server VAC status. 53 | required ServerVAC vac, 54 | /* TODO: Add TheShip flags */ 55 | 56 | /// Version of the game installed on the server. 57 | required String version, 58 | 59 | /// Server's port number. 60 | int? port, 61 | 62 | /// Server's SteamID. 63 | int? steamId, 64 | 65 | /// Spectator port number for SourceTV. 66 | int? tvPort, 67 | 68 | /// Name of the spectator server for SourceTV. 69 | String? tvName, 70 | 71 | /// Tags that describe the game according to the server (for future use.) 72 | String? keywords, 73 | 74 | /// The server's 64-bit GameID. 75 | int? gameId, 76 | }) = _ServerInfo; 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/rcon/rcon_packet.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:convert'; 4 | import 'dart:typed_data'; 5 | 6 | /* 7 | 3: SERVERDATA_AUTH 8 | 2: SERVERDATA_EXECCOMMAND 9 | 2: SERVERDATA_AUTH_RESPONSE 10 | 0: SERVERDATA_RESPONSE_VALUE 11 | */ 12 | class RconPacket { 13 | final Uint8List bytes; 14 | 15 | late final ByteData _byteData; 16 | 17 | int get size => _byteData.getInt32(0, Endian.little); 18 | 19 | int get id => _byteData.getInt32(4, Endian.little); 20 | 21 | int get type => _byteData.getInt32(8, Endian.little); 22 | 23 | Uint8List get body => bytes.sublist(12, bytes.length - 2); 24 | 25 | String get bodyAsString => utf8.decode(body, allowMalformed: true); 26 | 27 | bool get terminated => bytes.last == 0x00 && bytes[bytes.length -2] == 0x00; 28 | 29 | RconPacket(this.bytes) : _byteData = ByteData.view(bytes.buffer); 30 | 31 | factory RconPacket.from({ 32 | int id = 0, 33 | int type = 0, 34 | dynamic body = const [0x00], 35 | }) { 36 | assert((body is List && body.isNotEmpty) || body is String); 37 | if (body is String) { 38 | body = [...utf8.encode(body), 0x00]; 39 | } 40 | if (body is List) { 41 | final size = 4 + 4 + body.length + 1; 42 | final bytes = ByteData(size + 4); 43 | 44 | var offset = 0; 45 | bytes.setInt32(offset, size, Endian.little); 46 | offset += 4; 47 | 48 | bytes.setInt32(offset, id, Endian.little); 49 | offset += 4; 50 | 51 | bytes.setInt32(offset, type, Endian.little); 52 | offset += 4; 53 | 54 | bytes.setList(offset, body); 55 | 56 | return RconPacket(bytes.buffer.asUint8List()); 57 | } 58 | throw ArgumentError.value(body, 'body', 'Invalid type'); 59 | } 60 | 61 | factory RconPacket.auth({required String password, required int id}) { 62 | return RconPacket.from(body: password, type: 3, id: id); 63 | } 64 | 65 | factory RconPacket.command({required String command, required int id}) { 66 | return RconPacket.from(body: command, type: 2, id: id); 67 | } 68 | 69 | /// Used only to add content to body when receiving multi-packets replies. 70 | RconPacket expandBody(Iterable content) { 71 | assert(!terminated); 72 | return RconPacket(Uint8List.fromList([...bytes, ...content])); 73 | } 74 | } 75 | 76 | extension on ByteData { 77 | void setList(int byteOffset, List value) { 78 | for (var i = 0; i < value.length; i++) { 79 | setUint8(byteOffset + i, value[i]); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/source_server.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import '../source_server.dart'; 5 | 6 | /// Wrapper for both [RconSocket] and [QuerySocket] 7 | class SourceServer implements RconSocket, QuerySocket { 8 | /// The address used for the connection. 9 | final dynamic address; 10 | 11 | /// The port used for the connection. 12 | final int port; 13 | 14 | /// Timeout to wait for the remote server reply. 15 | final Duration timeout; 16 | 17 | final QuerySocket _querySocket; 18 | 19 | RconSocket? _rconSocket; 20 | 21 | SourceServer._( 22 | this.address, 23 | this.port, 24 | this._querySocket, 25 | this._rconSocket, 26 | this.timeout, 27 | ); 28 | 29 | /// Connects to the remote server using the server query protocol 30 | /// and the rcon protocol if the password was specified. 31 | /// This throws a [SocketException] if the authentication with the remote server failed. 32 | static Future connect( 33 | dynamic address, 34 | int port, { 35 | String? password, 36 | Duration timeout = const Duration(seconds: 30), 37 | }) async { 38 | final querySocket = await QuerySocket.connect(address, port); 39 | RconSocket? rconSocket; 40 | if (password != null) { 41 | rconSocket = await RconSocket.connect(address, port).timeout(timeout); 42 | final result = await rconSocket.authenticate(password).timeout(timeout); 43 | if (!result) { 44 | querySocket.close(); 45 | rconSocket.close(); 46 | throw RconAuthenticationException(rconSocket.errorMessage ?? ''); 47 | } 48 | } 49 | return SourceServer._(address, port, querySocket, rconSocket, timeout); 50 | } 51 | 52 | /// Authenticates to the remote server. 53 | /// Throws a [RconAuthenticationException] if the authentication fails. 54 | /// Can be used to connected to the rcon without specifying the password in [connect]. 55 | @override 56 | Future authenticate(String password) async { 57 | if (_rconSocket != null) { 58 | return true; 59 | } 60 | _rconSocket = await RconSocket.connect(address, port).timeout(timeout); 61 | final result = await _rconSocket!.authenticate(password).timeout(timeout); 62 | if (!result) { 63 | _rconSocket!.close(); 64 | throw RconAuthenticationException(_rconSocket!.errorMessage ?? ''); 65 | } 66 | return true; 67 | } 68 | 69 | @override 70 | void close() { 71 | _querySocket.close(); 72 | _rconSocket?.close(); 73 | } 74 | 75 | @override 76 | Future command(String command) { 77 | if (_rconSocket == null) { 78 | throw const RconException( 79 | 'Cannot send an RCON command while not authenticated!', 80 | ); 81 | } 82 | return _rconSocket!.command(command).timeout(timeout); 83 | } 84 | 85 | @override 86 | Stream get commandStream { 87 | assert(_rconSocket != null); 88 | return _rconSocket!.commandStream; 89 | } 90 | 91 | @override 92 | Future getInfo() => _querySocket.getInfo().timeout(timeout); 93 | 94 | @override 95 | Future> getPlayers() => 96 | _querySocket.getPlayers().timeout(timeout); 97 | 98 | @override 99 | Future> getRules() => 100 | _querySocket.getRules().timeout(timeout); 101 | 102 | @override 103 | String? get errorMessage => _rconSocket?.errorMessage; 104 | } 105 | -------------------------------------------------------------------------------- /test/dummy_server_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:source_server/source_server.dart'; 2 | import 'package:test/expect.dart'; 3 | import 'package:test/scaffolding.dart'; 4 | 5 | import 'dummy_server.dart'; 6 | 7 | void main() { 8 | // This test uses the following ports: 9123 - 9127 9 | group('Test query client', () { 10 | late DummyServer dummy; 11 | late SourceServer server; 12 | 13 | setUpAll(() async { 14 | dummy = await DummyServer.bind('127.0.0.1', port: 9123); 15 | server = await SourceServer.connect('127.0.0.1', 9123); 16 | }); 17 | 18 | tearDownAll(() async { 19 | server.close(); 20 | await dummy.close(); 21 | }); 22 | 23 | test('getInfo returns correct info', () async { 24 | final info = await server.getInfo(); 25 | 26 | expect(info, isA()); 27 | 28 | expect(info.protocol, 17); 29 | expect(info.name, 'Dummy server! - Join here!'); 30 | expect(info.map, 'de_dust2'); 31 | expect(info.folder, 'csgo'); 32 | expect(info.id, 730); 33 | expect(info.players, 16); 34 | expect(info.maxPlayers, 64); 35 | expect(info.bots, 5); 36 | expect(info.type, ServerType.dedicated); 37 | expect(info.os, ServerOS.linux); 38 | expect(info.visibility, ServerVisibility.public); 39 | expect(info.vac, ServerVAC.secured); 40 | expect(info.version, '1.37.9.4'); 41 | expect(info.port, 27015); 42 | expect(info.steamId, 10000000000000000); 43 | expect(info.tvPort, 27016); 44 | expect(info.tvName, 'Source TV'); 45 | expect(info.keywords, 'hello,world,nice,server'); 46 | expect(info.gameId, 730); 47 | }); 48 | 49 | test('getPlayers returns correct players', () async { 50 | final players = await server.getPlayers(); 51 | 52 | expect(players, isA>()); 53 | 54 | expect(players, hasLength(16)); 55 | 56 | final secondPlayer = players[1]; 57 | expect(secondPlayer.index, 0); 58 | expect(secondPlayer.name, 'Player - 1'); 59 | expect(secondPlayer.score, 3); 60 | expect(secondPlayer.duration, 5); 61 | }); 62 | 63 | test('getReturns returns correct rules', () async { 64 | final rules = await server.getRules(); 65 | 66 | expect(rules, isA>()); 67 | 68 | expect(rules, hasLength(8)); 69 | 70 | final rule = rules.first; 71 | expect(rule.name, 'rule_0'); 72 | expect(rule.value, 'value_0'); 73 | }); 74 | }); 75 | 76 | group('Test RCON client', () { 77 | test('Valid RCON password', () async { 78 | final dummy = 79 | await DummyServer.bind('127.0.0.1', port: 9124, password: '1234'); 80 | expect( 81 | await SourceServer.connect('127.0.0.1', 9124, password: '1234') 82 | ..close(), 83 | isA(), 84 | ); 85 | 86 | await dummy.close(); 87 | }); 88 | 89 | test('Invalid RCON password', () async { 90 | final dummy = 91 | await DummyServer.bind('127.0.0.1', port: 9125, password: '1234'); 92 | await expectLater( 93 | () async => SourceServer.connect('127.0.0.1', 9125, password: '12345'), 94 | throwsA(isA()), 95 | ); 96 | await dummy.close(); 97 | }); 98 | 99 | test('Correct RCON commands', () async { 100 | final dummy = 101 | await DummyServer.bind('127.0.0.1', port: 9126, password: '1234'); 102 | final server = 103 | await SourceServer.connect('127.0.0.1', 9126, password: '1234'); 104 | 105 | await expectLater(server.command('echo a'), completion('a \n')); 106 | await expectLater( 107 | server.command('hello world'), 108 | completion('Unknown command "hello"\n'), 109 | ); 110 | 111 | server.close(); 112 | await dummy.close(); 113 | }); 114 | 115 | test('Throws if not authenticated', () async { 116 | final dummy = 117 | await DummyServer.bind('127.0.0.1', port: 9127, password: '1234'); 118 | final server = await SourceServer.connect('127.0.0.1', 9127); 119 | 120 | await expectLater( 121 | () => server.command('echo a'), 122 | throwsA(isA()), 123 | ); 124 | 125 | server.close(); 126 | await dummy.close(); 127 | }); 128 | }); 129 | } 130 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/models/server_ping_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'server_ping_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_ServerPingInfo _$$_ServerPingInfoFromJson(Map json) => 10 | _$_ServerPingInfo( 11 | version: Version.fromJson(json['version'] as Map), 12 | players: Players.fromJson(json['players'] as Map), 13 | description: _descriptionToJson(json['description']), 14 | favicon: json['favicon'] as String?, 15 | modinfo: json['modinfo'] == null 16 | ? null 17 | : Modinfo.fromJson(json['modinfo'] as Map), 18 | ping: json['ping'] as int?, 19 | ); 20 | 21 | Map _$$_ServerPingInfoToJson(_$_ServerPingInfo instance) => 22 | { 23 | 'version': instance.version, 24 | 'players': instance.players, 25 | 'description': instance.description, 26 | 'favicon': instance.favicon, 27 | 'modinfo': instance.modinfo, 28 | 'ping': instance.ping, 29 | }; 30 | 31 | _$_Description _$$_DescriptionFromJson(Map json) => 32 | _$_Description( 33 | extra: (json['extra'] as List?) 34 | ?.map((e) => Extra.fromJson(e as Map)) 35 | .toList(), 36 | text: json['text'] as String, 37 | ); 38 | 39 | Map _$$_DescriptionToJson(_$_Description instance) => 40 | { 41 | 'extra': instance.extra, 42 | 'text': instance.text, 43 | }; 44 | 45 | _$_Extra _$$_ExtraFromJson(Map json) => _$_Extra( 46 | color: json['color'] as String?, 47 | text: json['text'] as String, 48 | bold: json['bold'] as bool?, 49 | strikethrough: json['strikethrough'] as bool?, 50 | italic: json['italic'] as bool?, 51 | underlined: json['underlined'] as bool?, 52 | ); 53 | 54 | Map _$$_ExtraToJson(_$_Extra instance) => { 55 | 'color': instance.color, 56 | 'text': instance.text, 57 | 'bold': instance.bold, 58 | 'strikethrough': instance.strikethrough, 59 | 'italic': instance.italic, 60 | 'underlined': instance.underlined, 61 | }; 62 | 63 | _$_Modinfo _$$_ModinfoFromJson(Map json) => _$_Modinfo( 64 | type: json['type'] as String, 65 | modList: (json['modList'] as List) 66 | .map((e) => Mod.fromJson(e as Map)) 67 | .toList(), 68 | ); 69 | 70 | Map _$$_ModinfoToJson(_$_Modinfo instance) => 71 | { 72 | 'type': instance.type, 73 | 'modList': instance.modList, 74 | }; 75 | 76 | _$_Players _$$_PlayersFromJson(Map json) => _$_Players( 77 | max: json['max'] as int, 78 | online: json['online'] as int, 79 | sample: (json['sample'] as List?) 80 | ?.map((e) => Sample.fromJson(e as Map)) 81 | .toList(), 82 | ); 83 | 84 | Map _$$_PlayersToJson(_$_Players instance) => 85 | { 86 | 'max': instance.max, 87 | 'online': instance.online, 88 | 'sample': instance.sample, 89 | }; 90 | 91 | _$_Sample _$$_SampleFromJson(Map json) => _$_Sample( 92 | id: json['id'] as String, 93 | name: json['name'] as String, 94 | ); 95 | 96 | Map _$$_SampleToJson(_$_Sample instance) => { 97 | 'id': instance.id, 98 | 'name': instance.name, 99 | }; 100 | 101 | _$_Version _$$_VersionFromJson(Map json) => _$_Version( 102 | name: json['name'] as String, 103 | protocol: json['protocol'] as int, 104 | ); 105 | 106 | Map _$$_VersionToJson(_$_Version instance) => 107 | { 108 | 'name': instance.name, 109 | 'protocol': instance.protocol, 110 | }; 111 | 112 | _$_Mod _$$_ModFromJson(Map json) => _$_Mod( 113 | modid: json['modid'] as String, 114 | version: json['version'] as String, 115 | ); 116 | 117 | Map _$$_ModToJson(_$_Mod instance) => { 118 | 'modid': instance.modid, 119 | 'version': instance.version, 120 | }; 121 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/models/server_ping_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'server_ping_info.freezed.dart'; 4 | 5 | part 'server_ping_info.g.dart'; 6 | 7 | /// Server info fetched using the `Server List Ping` interface. 8 | @freezed 9 | class ServerPingInfo with _$ServerPingInfo { 10 | /// Server info. 11 | const factory ServerPingInfo({ 12 | /// Server and protocol version. 13 | required Version version, 14 | 15 | /// Total and connected players. 16 | required Players players, 17 | 18 | /// MOTD description. 19 | // ignore: invalid_annotation_target 20 | @JsonKey(fromJson: _descriptionToJson) required Description description, 21 | 22 | /// Favicon data uri. 23 | String? favicon, 24 | 25 | /// Server's mods. 26 | Modinfo? modinfo, 27 | 28 | /// Server's ping. 29 | int? ping, 30 | }) = _ServerPingInfo; 31 | 32 | /// 33 | factory ServerPingInfo.fromJson(Map json) => 34 | _$ServerPingInfoFromJson(json); 35 | } 36 | 37 | /// MOTD description. 38 | @freezed 39 | class Description with _$Description { 40 | /// MOTD description. 41 | const factory Description({ 42 | /// Extra MOTD text. 43 | List? extra, 44 | 45 | /// Motd text. 46 | required String text, 47 | }) = _Description; 48 | 49 | /// 50 | factory Description.fromJson(Map json) => 51 | _$DescriptionFromJson(json); 52 | } 53 | 54 | /// Extra MOTD text. 55 | @freezed 56 | class Extra with _$Extra { 57 | /// Extra MOTD text. 58 | const factory Extra({ 59 | /// Text color. 60 | String? color, 61 | 62 | /// Actual text. 63 | required String text, 64 | 65 | /// Bold text. 66 | bool? bold, 67 | 68 | /// Strikethrough text. 69 | bool? strikethrough, 70 | 71 | /// Italic text. 72 | bool? italic, 73 | 74 | /// Underlined text. 75 | bool? underlined, 76 | }) = _Extra; 77 | 78 | /// 79 | factory Extra.fromJson(Map json) => _$ExtraFromJson(json); 80 | } 81 | 82 | /// Server's mods. 83 | @freezed 84 | class Modinfo with _$Modinfo { 85 | /// Server's mods. 86 | const factory Modinfo({ 87 | /// Mods type. 88 | required String type, 89 | 90 | /// Mods list. 91 | required List modList, 92 | }) = _Modinfo; 93 | 94 | /// 95 | factory Modinfo.fromJson(Map json) => 96 | _$ModinfoFromJson(json); 97 | } 98 | 99 | /// Total and connected players. 100 | @freezed 101 | class Players with _$Players { 102 | /// Total and connected players. 103 | const factory Players({ 104 | /// Max players. 105 | required int max, 106 | 107 | /// Connected players. 108 | required int online, 109 | 110 | /// Connected players list. 111 | List? sample, 112 | }) = _Players; 113 | 114 | /// 115 | factory Players.fromJson(Map json) => 116 | _$PlayersFromJson(json); 117 | } 118 | 119 | /// A server's player. 120 | @freezed 121 | class Sample with _$Sample { 122 | /// A server's player. 123 | const factory Sample({ 124 | /// Players' UUID. 125 | required String id, 126 | 127 | /// Players' name. 128 | required String name, 129 | }) = _Sample; 130 | 131 | /// 132 | factory Sample.fromJson(Map json) => _$SampleFromJson(json); 133 | } 134 | 135 | /// Version name and protocol number. 136 | @freezed 137 | class Version with _$Version { 138 | /// Version name and protocol number. 139 | const factory Version({ 140 | /// Version name. 141 | required String name, 142 | 143 | /// Version protocol. 144 | required int protocol, 145 | }) = _Version; 146 | 147 | /// 148 | factory Version.fromJson(Map json) => 149 | _$VersionFromJson(json); 150 | } 151 | 152 | /// Version name and protocol number. 153 | @freezed 154 | class Mod with _$Mod { 155 | /// Version name and protocol number. 156 | const factory Mod({ 157 | /// Mod id. 158 | required String modid, 159 | 160 | /// Mod version. 161 | required String version, 162 | }) = _Mod; 163 | 164 | /// 165 | factory Mod.fromJson(Map json) => _$ModFromJson(json); 166 | } 167 | 168 | Description _descriptionToJson(dynamic value) { 169 | if (value is String) { 170 | return Description(text: value); 171 | } 172 | if (value is Map) { 173 | value['text'] ??= value['translate'] ?? ''; 174 | return Description.fromJson(value); 175 | } 176 | throw TypeError(); 177 | } 178 | -------------------------------------------------------------------------------- /lib/src/buffer.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:convert'; 4 | import 'dart:typed_data'; 5 | 6 | class ReadBuffer { 7 | int _pos = 0; 8 | final ByteData data; 9 | Endian endian; 10 | 11 | ReadBuffer(this.data, [this.endian = Endian.little]); 12 | 13 | ReadBuffer.fromUint8List(Uint8List list, [this.endian = Endian.little]) 14 | : data = ByteData.view(list.buffer); 15 | 16 | int get uint8 => data.getUint8(_pos++); 17 | 18 | int get int8 => data.getInt8(_pos++); 19 | 20 | int get uint16 { 21 | final val = data.getUint16(_pos, endian); 22 | _pos += 2; 23 | return val; 24 | } 25 | 26 | int get int16 { 27 | final val = data.getInt16(_pos, endian); 28 | _pos += 2; 29 | return val; 30 | } 31 | 32 | int get uint32 { 33 | final val = data.getUint32(_pos, endian); 34 | _pos += 4; 35 | return val; 36 | } 37 | 38 | int get int32 { 39 | final val = data.getInt32(_pos, endian); 40 | _pos += 4; 41 | return val; 42 | } 43 | 44 | int get uint64 { 45 | final val = data.getUint64(_pos, endian); 46 | _pos += 8; 47 | return val; 48 | } 49 | 50 | int get int64 { 51 | final val = data.getInt64(_pos, endian); 52 | _pos += 8; 53 | return val; 54 | } 55 | 56 | double get float32 { 57 | final val = data.getFloat32(_pos, endian); 58 | _pos += 4; 59 | return val; 60 | } 61 | 62 | double get float64 { 63 | final val = data.getFloat64(_pos, endian); 64 | _pos += 8; 65 | return val; 66 | } 67 | 68 | int peek(int pos) => data.getUint8(_pos + pos); 69 | 70 | void skip(int count) => _pos += count; 71 | 72 | String get string { 73 | final bytes = []; 74 | for (var val = uint8;; val = uint8) { 75 | if (val == 0) { 76 | break; 77 | } 78 | bytes.add(val); 79 | } 80 | return utf8.decode(bytes); 81 | } 82 | 83 | bool get canReadMore => _pos < data.lengthInBytes; 84 | } 85 | 86 | class WriteBuffer { 87 | int _pos = 0; 88 | 89 | final bool fixedSize; 90 | final int startSize; 91 | 92 | late ByteData data; 93 | 94 | WriteBuffer(this.startSize, {this.fixedSize = false}) 95 | : data = ByteData(startSize); 96 | 97 | int _checkSize(int length, {bool updatePos = true}) { 98 | final oldPos = _pos; 99 | if (_pos >= data.lengthInBytes) { 100 | if (fixedSize) { 101 | throw RangeError.value(length); 102 | } else { 103 | final l = data.buffer 104 | .asUint8List() 105 | .followedBy(List.generate(length, (index) => 0)) 106 | .toList(); 107 | data = ByteData.view(Uint8List.fromList(l).buffer); 108 | } 109 | } 110 | 111 | if (updatePos) { 112 | _pos += length; 113 | } 114 | return oldPos; 115 | } 116 | 117 | void writeUint8(int value) { 118 | final pos = _checkSize(1); 119 | data.setUint8(pos, value); 120 | } 121 | 122 | void writeInt8(int value) { 123 | final pos = _checkSize(1); 124 | data.setInt8(pos, value); 125 | } 126 | 127 | void writeUint16(int value, [Endian endian = Endian.little]) { 128 | final pos = _checkSize(2); 129 | data.setUint16(pos, value, endian); 130 | } 131 | 132 | void writeInt16(int value) { 133 | final pos = _checkSize(2); 134 | data.setInt16(pos, value, Endian.little); 135 | } 136 | 137 | void writeUint32(int value) { 138 | final pos = _checkSize(4); 139 | data.setUint32(pos, value, Endian.little); 140 | } 141 | 142 | void writeInt32(int value) { 143 | final pos = _checkSize(4); 144 | data.setInt32(pos, value, Endian.little); 145 | } 146 | 147 | void writeUint64(int value, [Endian endian = Endian.little]) { 148 | final pos = _checkSize(8); 149 | data.setUint64(pos, value, endian); 150 | } 151 | 152 | void writeInt64(int value) { 153 | final pos = _checkSize(8); 154 | data.setInt64(pos, value, Endian.little); 155 | } 156 | 157 | void writeFloat32(double value) { 158 | final pos = _checkSize(4); 159 | data.setFloat32(pos, value, Endian.little); 160 | } 161 | 162 | void writeFloat64(double value) { 163 | final pos = _checkSize(8); 164 | data.setFloat64(pos, value, Endian.little); 165 | } 166 | 167 | void writeString(String value, {bool nullTerminated = true}) { 168 | final str = utf8.encode(value); 169 | _checkSize(str.length + (nullTerminated ? 1 : 0), updatePos: false); 170 | for (final v in str) { 171 | writeUint8(v); 172 | } 173 | if (nullTerminated) { 174 | writeUint8(0); 175 | } 176 | } 177 | 178 | void writeBytes(List bytes) { 179 | _checkSize(bytes.length, updatePos: false); 180 | for (final byte in bytes) { 181 | writeUint8(byte); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /lib/src/minecraft_server.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import '../minecraft_server.dart'; 5 | 6 | /// Wrapper for both [RconSocket] and [QuerySocket] 7 | class MinecraftServer implements RconSocket, QuerySocket, ListPingSocket { 8 | /// The address used for the connection. 9 | final dynamic address; 10 | 11 | /// The port used for the connection. 12 | final int port; 13 | 14 | /// Timeout to wait for the remote server reply. 15 | final Duration timeout; 16 | 17 | final ListPingSocket _listPingSocket; 18 | final QuerySocket? _querySocket; 19 | RconSocket? _rconSocket; 20 | 21 | MinecraftServer._( 22 | this.address, 23 | this.port, 24 | this._listPingSocket, 25 | this._querySocket, 26 | this._rconSocket, 27 | this.timeout, 28 | ); 29 | 30 | /// Connects to the remote server using the `server ping protocol`. 31 | /// The query connection is established only if the [queryPort] is not null, 32 | /// and the same applies to the rcon connection which requires both the [rconPort] and [password] to be non null. 33 | /// This throws a [SocketException] if the authentication with the remote server failed. 34 | static Future connect( 35 | dynamic address, 36 | int port, { 37 | int? queryPort, 38 | int? rconPort, 39 | String? password, 40 | Duration timeout = const Duration(seconds: 30), 41 | }) async { 42 | final listPingSocket = await ListPingSocket.connect(address, port); 43 | 44 | QuerySocket? querySocket; 45 | RconSocket? rconSocket; 46 | 47 | if (queryPort != null) { 48 | querySocket = await QuerySocket.connect(address, queryPort); 49 | } 50 | if (rconPort != null && password != null) { 51 | rconSocket = await RconSocket.connect(address, rconPort).timeout(timeout); 52 | final result = await rconSocket.authenticate(password).timeout(timeout); 53 | if (!result) { 54 | listPingSocket.close(); 55 | querySocket?.close(); 56 | rconSocket.close(); 57 | throw RconAuthenticationException(rconSocket.errorMessage ?? ''); 58 | } 59 | } 60 | return MinecraftServer._( 61 | address, 62 | port, 63 | listPingSocket, 64 | querySocket, 65 | rconSocket, 66 | timeout, 67 | ); 68 | } 69 | 70 | /// Authenticates to the remote server. 71 | /// Throws a [RconAuthenticationException] if the authentication fails. 72 | /// Can be used to connected to the rcon without specifying the password in [connect]. 73 | /// If the [rconPort] is null, the [port] is used for the connection. 74 | @override 75 | Future authenticate(String password, {int? rconPort}) async { 76 | if (_rconSocket != null) { 77 | return true; 78 | } 79 | _rconSocket = 80 | await RconSocket.connect(address, rconPort ?? port).timeout(timeout); 81 | final result = await _rconSocket!.authenticate(password).timeout(timeout); 82 | if (!result) { 83 | _rconSocket!.close(); 84 | throw RconAuthenticationException(_rconSocket!.errorMessage ?? ''); 85 | } 86 | return true; 87 | } 88 | 89 | @override 90 | void close() { 91 | _listPingSocket.close(); 92 | _querySocket?.close(); 93 | _rconSocket?.close(); 94 | } 95 | 96 | @override 97 | Future command(String command) { 98 | if (_rconSocket == null) { 99 | throw const RconException( 100 | 'Cannot send an RCON command while not authenticated!', 101 | ); 102 | } 103 | return _rconSocket!.command(command).timeout(timeout); 104 | } 105 | 106 | @override 107 | Stream get commandStream { 108 | if (_rconSocket == null) { 109 | throw const RconException( 110 | 'Cannot listen to the commandStream if the RCON is not authenticated.', 111 | ); 112 | } 113 | return _rconSocket!.commandStream; 114 | } 115 | 116 | @override 117 | Future getInfo() { 118 | if (_querySocket == null) { 119 | throw const QueryException('Query socket not connected.'); 120 | } 121 | return _querySocket!.getInfo().timeout(timeout); 122 | } 123 | 124 | @override 125 | Future getFullInfo() { 126 | if (_querySocket == null) { 127 | throw const QueryException('Query socket not connected.'); 128 | } 129 | return _querySocket!.getFullInfo().timeout(timeout); 130 | } 131 | 132 | @override 133 | String? get errorMessage => _rconSocket?.errorMessage; 134 | 135 | @override 136 | Future getPingInfo({bool requestPing = true}) => 137 | _listPingSocket.getPingInfo(requestPing: requestPing).timeout(timeout); 138 | 139 | @override 140 | 141 | /// True if the *ping socket* is connected. 142 | bool get isConnected => _listPingSocket.isConnected; 143 | } 144 | -------------------------------------------------------------------------------- /lib/src/rcon/rcon_socket.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | import 'dart:convert'; 4 | import 'dart:io'; 5 | import 'dart:typed_data'; 6 | 7 | import 'package:logging/logging.dart'; 8 | 9 | import '../exceptions/exceptions.dart'; 10 | import 'rcon_packet.dart'; 11 | 12 | /// Allows the connection to a remote server using the rcon protocol. 13 | /// If the current ip address is banned no reply will be sent and the authentication will hang forever. 14 | abstract class RconSocket { 15 | factory RconSocket._(Socket socket) = _RconSocketImpl; 16 | 17 | /// Authenticates to the remote server. 18 | /// Returns false if the authentication failed, true otherwise. 19 | Future authenticate(String password); 20 | 21 | /// Executes a command on the remote server. 22 | Future command(String command); 23 | 24 | /// Stream of command reply from the server. 25 | /// Useful if you don't need to wait to the replies and want to get 26 | /// the multi-packets reply too. 27 | Stream get commandStream; 28 | 29 | /// If the authentication failed this is might contain a more descriptive message. 30 | String? get errorMessage; 31 | 32 | /// Closes the connection 33 | void close(); 34 | 35 | /// Setup the connection to the remote server. 36 | static Future connect(dynamic address, int port) async { 37 | assert(address is String || address is InternetAddress); 38 | if (address is String) { 39 | // ignore: parameter_assignments 40 | address = (await InternetAddress.lookup(address)).first; 41 | } 42 | 43 | final socket = await Socket.connect(address, port); 44 | return RconSocket._(socket); 45 | } 46 | } 47 | 48 | class _RconSocketImpl implements RconSocket { 49 | late final Logger logger = 50 | Logger('RconSocket(${socket.address.address}:${socket.port})'); 51 | 52 | final Socket socket; 53 | final StreamController _commandStream = StreamController(); 54 | 55 | String? _errorMessage; 56 | 57 | @override 58 | Stream get commandStream => _commandStream.stream; 59 | 60 | @override 61 | String? get errorMessage => _errorMessage; 62 | 63 | Completer? authCompleter; 64 | final HashMap> cmdMap = 65 | HashMap>(); 66 | 67 | bool skip = true; 68 | int packetId = 2; 69 | bool? authStatus; 70 | late RconPacket _buffer; 71 | 72 | 73 | _RconSocketImpl(this.socket) { 74 | socket.listen(onEvent); 75 | } 76 | 77 | @override 78 | Future authenticate(String password) async { 79 | if (authCompleter?.isCompleted ?? false) { 80 | assert(authStatus != null); 81 | return authStatus!; 82 | } 83 | if (authCompleter != null) { 84 | return authCompleter!.future; 85 | } 86 | 87 | if (password.isEmpty) { 88 | throw ArgumentError.value(password, 'password', 'Cannot be empty!'); 89 | } 90 | assert(authCompleter == null); 91 | 92 | authCompleter = Completer(); 93 | final packet = RconPacket.auth(password: password, id: 1).bytes; 94 | socket.add(packet); 95 | logger.info('Sent authentication packet'); 96 | logger.fine('Authentication packet(${packet.length}):\n$packet\n'); 97 | return authCompleter!.future; 98 | } 99 | 100 | @override 101 | Future command(String command) { 102 | if (!(authStatus ?? false)) { 103 | throw const RconException( 104 | 'Cannot send an RCON command while not authenticated!', 105 | ); 106 | } 107 | final resultCompleter = Completer(); 108 | 109 | cmdMap[packetId] = resultCompleter; 110 | 111 | final packet = RconPacket.command(command: command, id: packetId++).bytes; 112 | socket.add(packet); 113 | logger.info('Sent command: $command'); 114 | logger.fine('Command packet(${packet.length}):\n$packet\n'); 115 | 116 | return resultCompleter.future; 117 | } 118 | 119 | @override 120 | void close() { 121 | socket.destroy(); 122 | } 123 | 124 | void onEvent(Uint8List event) { 125 | final packet = RconPacket(event); 126 | if (packet.id == 0) { 127 | return; 128 | } 129 | 130 | if (packet.type == 2) { 131 | authStatus = packet.id != -1; 132 | authCompleter!.complete(packet.id != -1); 133 | } else if (packet.type == 0) { 134 | // Auth packet 135 | if (packet.id == 1) { 136 | _errorMessage = packet.bodyAsString; 137 | return; 138 | } 139 | final body = packet.bodyAsString; 140 | _commandStream.add(body); 141 | 142 | // Check if this a multi-packet reply or not. 143 | if (packet.terminated) { 144 | cmdMap.remove(packet.id)?.complete(body); 145 | return; 146 | } 147 | 148 | // This is a multi-packet reply, 149 | // save the packet for when we have the full packet. 150 | _buffer = packet; 151 | return; 152 | } else { 153 | // Immediately send the text to the command stream 154 | _commandStream.add( 155 | utf8.decode( 156 | [ 157 | for (final byte in packet.bytes) // Filter the null bytes 158 | if (byte != 0x00) byte 159 | ], 160 | allowMalformed: true, 161 | ), 162 | ); 163 | 164 | // Expand the buffer and if it's terminated complete the command's future. 165 | _buffer = _buffer.expandBody(packet.bytes); 166 | if (packet.terminated) { 167 | cmdMap.remove(_buffer.id)?.complete(_buffer.bodyAsString); 168 | return; 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/list_ping_socket.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'dart:typed_data'; 5 | 6 | import 'package:logging/logging.dart'; 7 | 8 | import '../../buffer.dart'; 9 | import 'models/server_ping_info.dart'; 10 | 11 | /// Allows the connection to a remote server using the rcon protocol. 12 | /// If the current ip address is banned no reply will be sent and the authentication will hang forever. 13 | abstract class ListPingSocket { 14 | factory ListPingSocket._(Socket socket) = _ListPingSocket; 15 | 16 | /// True if the server is connected. 17 | bool get isConnected; 18 | 19 | /// Fetches the server info such a the MOTD, total number of players and other. 20 | /// Note: After this method is run the connection to the socket is closed, and the result is cached. 21 | /// if you want to refresh the result you should create a new socket connection. 22 | Future getPingInfo({bool requestPing = true}); 23 | 24 | /// Closes the connection 25 | void close(); 26 | 27 | /// Setup the connection to the remote server. 28 | static Future connect(dynamic address, int port) async { 29 | assert(address is String || address is InternetAddress); 30 | final socket = await Socket.connect(address, port); 31 | return ListPingSocket._(socket); 32 | } 33 | } 34 | 35 | class _ListPingSocket implements ListPingSocket { 36 | @override 37 | bool isConnected = true; 38 | 39 | late final Logger logger = 40 | Logger('ListPingSocket(${socket.address.address}:${socket.port})'); 41 | 42 | final Socket socket; 43 | final StringBuffer buffer = StringBuffer(); 44 | 45 | Completer? infoCompleter; 46 | 47 | _ListPingSocket(this.socket) { 48 | socket.listen(onEvent).onDone(() { 49 | isConnected = false; 50 | }); 51 | } 52 | 53 | @override 54 | void close() { 55 | isConnected = false; 56 | socket.destroy(); 57 | } 58 | 59 | bool requestPing = true; 60 | ServerPingInfo? info; 61 | 62 | void onEvent(Uint8List event) { 63 | logger.fine('Received packet(${event.length}):\n$event\n'); 64 | final size = _readVarInt(event); 65 | 66 | final id = _readVarInt(event.skip(size.length)); 67 | if (id.value == -1) { 68 | throw Exception('Invalid packet id.'); 69 | } 70 | 71 | if (id.value == 0) { 72 | final length = _readVarInt(event.skip(id.length + size.length)); 73 | 74 | final str = 75 | String.fromCharCodes(event, id.length + size.length + length.length); 76 | 77 | buffer.write(str); 78 | 79 | final decoded = json.decode(buffer.toString()) as Map; 80 | if ((decoded['translate'] as String?)?.contains('disconnect') ?? false) { 81 | throw SocketException('${decoded['translate']}: ${decoded['with']}'); 82 | } 83 | 84 | final info = ServerPingInfo.fromJson(decoded); 85 | if (requestPing) { 86 | this.info = info; 87 | 88 | final write = WriteBuffer(9, fixedSize: true); 89 | write.writeUint8(0x01); 90 | write.writeUint64( 91 | DateTime.now().millisecondsSinceEpoch, 92 | Endian.big, 93 | ); 94 | 95 | final decoded = write.data.buffer.asUint8List(); 96 | final packet = [..._writeVarNumber(decoded.length), ...decoded]; 97 | 98 | socket.add(packet); 99 | } else { 100 | infoCompleter!.complete(info); 101 | close(); 102 | } 103 | } 104 | if (id.value == 1) { 105 | final buffer = ReadBuffer.fromUint8List( 106 | Uint8List.fromList(event.skip(2).toList()), 107 | // Skip the packet length and id. 108 | Endian.big, 109 | ); // Skip the packet length and id. 110 | 111 | infoCompleter!.complete( 112 | info!.copyWith( 113 | ping: DateTime.now().millisecondsSinceEpoch - buffer.uint64, 114 | ), 115 | ); 116 | close(); 117 | } 118 | } 119 | 120 | @override 121 | Future getPingInfo({bool requestPing = true}) { 122 | if (infoCompleter != null) { 123 | return infoCompleter!.future; 124 | } 125 | this.requestPing = requestPing; 126 | 127 | infoCompleter = Completer(); 128 | 129 | final write = WriteBuffer(15); 130 | write.writeUint8(0x00); 131 | write.writeBytes(_writeVarNumber(4)); 132 | write.writeBytes(_writeVarNumber(socket.address.address.length)); 133 | write.writeBytes(socket.address.address.codeUnits); 134 | write.writeUint16(socket.remotePort, Endian.big); 135 | write.writeBytes(_writeVarNumber(1)); 136 | 137 | final decoded = write.data.buffer.asUint8List(); 138 | final packet = [..._writeVarNumber(decoded.length), ...decoded, 0x01, 0x00]; 139 | 140 | socket.add(packet); 141 | return infoCompleter!.future; 142 | } 143 | } 144 | 145 | _VarInt _readVarInt(Iterable bytes) { 146 | var decodedInt = 0; 147 | var bitOffset = 0; 148 | var length = 0; 149 | 150 | for (final byte in bytes) { 151 | length++; 152 | decodedInt |= (byte & 127) << bitOffset; 153 | 154 | if (bitOffset == 35) throw Exception('VarInt is too big'); 155 | if ((byte & 128) == 0) { 156 | break; 157 | } 158 | bitOffset += 7; 159 | } 160 | 161 | return _VarInt(decodedInt, length); 162 | } 163 | 164 | List _writeVarNumber(int value) { 165 | final bytes = []; 166 | 167 | for (;;) { 168 | if ((value & 0xFFFFFF80) == 0) { 169 | bytes.add(value); 170 | return bytes; 171 | } 172 | 173 | bytes.add(value & 0x7F | 0x80); 174 | // ignore: parameter_assignments 175 | value >>>= 7; 176 | } 177 | } 178 | 179 | class _VarInt { 180 | final int value; 181 | final int length; 182 | 183 | const _VarInt(this.value, this.length); 184 | } 185 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/query_socket.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'dart:typed_data'; 5 | 6 | import '../../buffer.dart'; 7 | import '../../exceptions/exceptions.dart'; 8 | import 'models/server_info.dart'; 9 | import 'query_packet.dart'; 10 | 11 | /// Parsed map of a ServerFullInfo reply. 12 | /// See [QuerySocket.getFullInfo]. 13 | typedef ServerFullInfo = Map; 14 | 15 | /// Wrapper for the query protocol. 16 | abstract class QuerySocket { 17 | factory QuerySocket._( 18 | InternetAddress address, 19 | int port, 20 | RawDatagramSocket socket, 21 | ) = _QuerySocketImpl; 22 | 23 | /// Returns the info about this server. 24 | /// See [getFullInfo] 25 | Future getInfo(); 26 | 27 | /// Returns more complete info about this server. 28 | /// See [getInfo] 29 | Future getFullInfo(); 30 | 31 | /// Closes the connection 32 | void close(); 33 | 34 | /// Setup the connection to the remote server. 35 | /// This does not guarantee that the connection will be established successfully. 36 | static Future connect( 37 | dynamic address, 38 | int port, { 39 | int localPort = 0, 40 | }) async { 41 | assert(address is String || address is InternetAddress); 42 | if (address is String) { 43 | // ignore: parameter_assignments 44 | address = (await InternetAddress.lookup(address)).first; 45 | } 46 | 47 | final socket = 48 | await RawDatagramSocket.bind(InternetAddress.anyIPv4, localPort); 49 | return QuerySocket._(address as InternetAddress, port, socket); 50 | } 51 | } 52 | 53 | class _QuerySocketImpl implements QuerySocket { 54 | final InternetAddress address; 55 | final int port; 56 | final RawDatagramSocket socket; 57 | 58 | Completer? infoCompleter; 59 | Completer? fullInfoCompleter; 60 | Completer>? challengeCompleter; 61 | 62 | _QuerySocketImpl(this.address, this.port, this.socket) { 63 | socket.listen(onEvent); 64 | } 65 | 66 | Future> getChallenge() async { 67 | assert(!(challengeCompleter?.isCompleted ?? false)); 68 | if (challengeCompleter != null) { 69 | return challengeCompleter!.future; 70 | } 71 | challengeCompleter = Completer(); 72 | socket.send(QueryPacket.challenge.bytes, address, port); 73 | return challengeCompleter!.future; 74 | } 75 | 76 | @override 77 | Future getInfo() { 78 | assert(!(infoCompleter?.isCompleted ?? false)); 79 | if (infoCompleter != null) { 80 | return infoCompleter!.future; 81 | } 82 | 83 | infoCompleter = Completer(); 84 | 85 | getChallenge().then( 86 | (value) => socket.send( 87 | QueryPacket.info(value).bytes, 88 | address, 89 | port, 90 | ), 91 | ); 92 | 93 | return infoCompleter!.future; 94 | } 95 | 96 | @override 97 | Future getFullInfo() { 98 | assert(!(fullInfoCompleter?.isCompleted ?? false)); 99 | if (fullInfoCompleter != null) { 100 | return fullInfoCompleter!.future; 101 | } 102 | 103 | fullInfoCompleter = Completer(); 104 | 105 | getChallenge().then( 106 | (value) => socket.send( 107 | QueryPacket.fullInfo(value).bytes, 108 | address, 109 | port, 110 | ), 111 | ); 112 | 113 | return fullInfoCompleter!.future; 114 | } 115 | 116 | void parseChallenge(Uint8List bytes) { 117 | final strValue = utf8.decode(bytes.takeWhile((e) => e != 0).toList()); 118 | final value = int.parse(strValue); 119 | 120 | challengeCompleter!.complete(value.toInt32()); 121 | challengeCompleter = null; 122 | } 123 | 124 | void parseInfo(Uint8List bytes) { 125 | final read = ReadBuffer.fromUint8List(bytes); 126 | 127 | final motd = read.string; 128 | final gametype = read.string; 129 | final map = read.string; 130 | final numPlayers = int.parse(read.string); 131 | final maxPlayers = int.parse(read.string); 132 | final port = read.int16; 133 | final ip = read.string; 134 | 135 | infoCompleter!.complete( 136 | ServerInfo( 137 | motd: motd, 138 | gametype: gametype, 139 | map: map, 140 | players: numPlayers, 141 | maxPlayers: maxPlayers, 142 | port: port, 143 | ip: ip, 144 | ), 145 | ); 146 | infoCompleter = null; 147 | } 148 | 149 | void parseFullInfo(Uint8List bytes) { 150 | final read = ReadBuffer.fromUint8List(bytes.sublist(15)); 151 | final info = {}; 152 | while (read.peek(0) != 0 && read.peek(1) != 0) { 153 | final key = read.string; 154 | final value = read.string; 155 | info[key] = value; 156 | } 157 | read.skip(2); 158 | 159 | fullInfoCompleter!.complete(info); 160 | fullInfoCompleter = null; 161 | } 162 | 163 | void onEvent(RawSocketEvent event) { 164 | if (event != RawSocketEvent.read) { 165 | return; 166 | } 167 | final datagram = socket.receive(); 168 | if (datagram == null) { 169 | return; 170 | } 171 | 172 | final data = datagram.data; 173 | 174 | final type = data[0]; 175 | 176 | if (type == 0x09) { 177 | parseChallenge(data.sublist(5)); 178 | } else if (type == 0x00) { 179 | // Check if contains the "full info" padding. Checking the first two indexes should be enough. 180 | if (data[5] == 0x73 && data[6] == 0x70) { 181 | parseFullInfo(data.sublist(5)); 182 | } else { 183 | parseInfo(data.sublist(5)); 184 | } 185 | } else { 186 | throw QueryException('Unrecognized type: $type'); 187 | } 188 | } 189 | 190 | @override 191 | void close() => socket.close(); 192 | } 193 | 194 | extension on int { 195 | List toInt32() { 196 | final byteData = ByteData(4); 197 | byteData.setInt32(0, this); 198 | return byteData.buffer.asUint8List(); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /lib/src/query/source/models/query_player.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'query_player.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$QueryPlayer { 19 | /// Index of player. 20 | int get index => throw _privateConstructorUsedError; 21 | 22 | /// Name of the player. 23 | String get name => throw _privateConstructorUsedError; 24 | 25 | /// Player's score (usually "frags" or "kills".) 26 | int get score => throw _privateConstructorUsedError; 27 | 28 | /// Time (in seconds) player has been connected to the server. 29 | double get duration => throw _privateConstructorUsedError; 30 | 31 | @JsonKey(ignore: true) 32 | $QueryPlayerCopyWith get copyWith => 33 | throw _privateConstructorUsedError; 34 | } 35 | 36 | /// @nodoc 37 | abstract class $QueryPlayerCopyWith<$Res> { 38 | factory $QueryPlayerCopyWith( 39 | QueryPlayer value, $Res Function(QueryPlayer) then) = 40 | _$QueryPlayerCopyWithImpl<$Res>; 41 | $Res call({int index, String name, int score, double duration}); 42 | } 43 | 44 | /// @nodoc 45 | class _$QueryPlayerCopyWithImpl<$Res> implements $QueryPlayerCopyWith<$Res> { 46 | _$QueryPlayerCopyWithImpl(this._value, this._then); 47 | 48 | final QueryPlayer _value; 49 | // ignore: unused_field 50 | final $Res Function(QueryPlayer) _then; 51 | 52 | @override 53 | $Res call({ 54 | Object? index = freezed, 55 | Object? name = freezed, 56 | Object? score = freezed, 57 | Object? duration = freezed, 58 | }) { 59 | return _then(_value.copyWith( 60 | index: index == freezed 61 | ? _value.index 62 | : index // ignore: cast_nullable_to_non_nullable 63 | as int, 64 | name: name == freezed 65 | ? _value.name 66 | : name // ignore: cast_nullable_to_non_nullable 67 | as String, 68 | score: score == freezed 69 | ? _value.score 70 | : score // ignore: cast_nullable_to_non_nullable 71 | as int, 72 | duration: duration == freezed 73 | ? _value.duration 74 | : duration // ignore: cast_nullable_to_non_nullable 75 | as double, 76 | )); 77 | } 78 | } 79 | 80 | /// @nodoc 81 | abstract class _$$_QueryPlayerCopyWith<$Res> 82 | implements $QueryPlayerCopyWith<$Res> { 83 | factory _$$_QueryPlayerCopyWith( 84 | _$_QueryPlayer value, $Res Function(_$_QueryPlayer) then) = 85 | __$$_QueryPlayerCopyWithImpl<$Res>; 86 | @override 87 | $Res call({int index, String name, int score, double duration}); 88 | } 89 | 90 | /// @nodoc 91 | class __$$_QueryPlayerCopyWithImpl<$Res> extends _$QueryPlayerCopyWithImpl<$Res> 92 | implements _$$_QueryPlayerCopyWith<$Res> { 93 | __$$_QueryPlayerCopyWithImpl( 94 | _$_QueryPlayer _value, $Res Function(_$_QueryPlayer) _then) 95 | : super(_value, (v) => _then(v as _$_QueryPlayer)); 96 | 97 | @override 98 | _$_QueryPlayer get _value => super._value as _$_QueryPlayer; 99 | 100 | @override 101 | $Res call({ 102 | Object? index = freezed, 103 | Object? name = freezed, 104 | Object? score = freezed, 105 | Object? duration = freezed, 106 | }) { 107 | return _then(_$_QueryPlayer( 108 | index: index == freezed 109 | ? _value.index 110 | : index // ignore: cast_nullable_to_non_nullable 111 | as int, 112 | name: name == freezed 113 | ? _value.name 114 | : name // ignore: cast_nullable_to_non_nullable 115 | as String, 116 | score: score == freezed 117 | ? _value.score 118 | : score // ignore: cast_nullable_to_non_nullable 119 | as int, 120 | duration: duration == freezed 121 | ? _value.duration 122 | : duration // ignore: cast_nullable_to_non_nullable 123 | as double, 124 | )); 125 | } 126 | } 127 | 128 | /// @nodoc 129 | 130 | class _$_QueryPlayer implements _QueryPlayer { 131 | _$_QueryPlayer( 132 | {required this.index, 133 | required this.name, 134 | required this.score, 135 | required this.duration}); 136 | 137 | /// Index of player. 138 | @override 139 | final int index; 140 | 141 | /// Name of the player. 142 | @override 143 | final String name; 144 | 145 | /// Player's score (usually "frags" or "kills".) 146 | @override 147 | final int score; 148 | 149 | /// Time (in seconds) player has been connected to the server. 150 | @override 151 | final double duration; 152 | 153 | @override 154 | String toString() { 155 | return 'QueryPlayer(index: $index, name: $name, score: $score, duration: $duration)'; 156 | } 157 | 158 | @override 159 | bool operator ==(dynamic other) { 160 | return identical(this, other) || 161 | (other.runtimeType == runtimeType && 162 | other is _$_QueryPlayer && 163 | const DeepCollectionEquality().equals(other.index, index) && 164 | const DeepCollectionEquality().equals(other.name, name) && 165 | const DeepCollectionEquality().equals(other.score, score) && 166 | const DeepCollectionEquality().equals(other.duration, duration)); 167 | } 168 | 169 | @override 170 | int get hashCode => Object.hash( 171 | runtimeType, 172 | const DeepCollectionEquality().hash(index), 173 | const DeepCollectionEquality().hash(name), 174 | const DeepCollectionEquality().hash(score), 175 | const DeepCollectionEquality().hash(duration)); 176 | 177 | @JsonKey(ignore: true) 178 | @override 179 | _$$_QueryPlayerCopyWith<_$_QueryPlayer> get copyWith => 180 | __$$_QueryPlayerCopyWithImpl<_$_QueryPlayer>(this, _$identity); 181 | } 182 | 183 | abstract class _QueryPlayer implements QueryPlayer { 184 | factory _QueryPlayer( 185 | {required final int index, 186 | required final String name, 187 | required final int score, 188 | required final double duration}) = _$_QueryPlayer; 189 | 190 | @override 191 | 192 | /// Index of player. 193 | int get index => throw _privateConstructorUsedError; 194 | @override 195 | 196 | /// Name of the player. 197 | String get name => throw _privateConstructorUsedError; 198 | @override 199 | 200 | /// Player's score (usually "frags" or "kills".) 201 | int get score => throw _privateConstructorUsedError; 202 | @override 203 | 204 | /// Time (in seconds) player has been connected to the server. 205 | double get duration => throw _privateConstructorUsedError; 206 | @override 207 | @JsonKey(ignore: true) 208 | _$$_QueryPlayerCopyWith<_$_QueryPlayer> get copyWith => 209 | throw _privateConstructorUsedError; 210 | } 211 | -------------------------------------------------------------------------------- /test/dummy_server.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:collection/collection.dart'; 5 | import 'package:source_server/source_server.dart'; 6 | import 'package:source_server/src/buffer.dart'; 7 | import 'package:source_server/src/query/source/query_packet.dart'; 8 | import 'package:source_server/src/rcon/rcon_packet.dart'; 9 | 10 | class DummyServer { 11 | final RawDatagramSocket udpSocket; 12 | final ServerSocket tcpSocket; 13 | final String password; 14 | 15 | DummyServer._(this.tcpSocket, this.udpSocket, this.password) { 16 | udpSocket.listen(onQueryData); 17 | tcpSocket.listen(newSocket); 18 | } 19 | 20 | static Future bind( 21 | dynamic address, { 22 | int port = 27015, 23 | String password = '', 24 | }) async { 25 | final tcpSocket = await ServerSocket.bind(address, port); 26 | final udpSocket = await RawDatagramSocket.bind(address, port); 27 | return DummyServer._(tcpSocket, udpSocket, password); 28 | } 29 | 30 | static const _eq = IterableEquality(); 31 | static const _emptyPacket = [0xFF, 0xFF, 0xFF, 0xFF]; 32 | static const _challengePacket = [0xA1, 0xD4, 0x87, 0xE2]; 33 | 34 | Future close() async { 35 | udpSocket.close(); 36 | await tcpSocket.close(); 37 | } 38 | 39 | /* UDP SECTION - QUERY */ 40 | void onQueryData(RawSocketEvent event) { 41 | final datagram = udpSocket.receive(); 42 | if (datagram == null) { 43 | return; 44 | } 45 | 46 | final bytes = datagram.data; 47 | final packet = QueryPacket(bytes); 48 | 49 | if (bytes.length < 5) { 50 | return; 51 | } 52 | 53 | if (!_eq.equals(bytes.sublist(0, 4), _emptyPacket)) { 54 | return; 55 | } 56 | 57 | final header = bytes[4]; 58 | 59 | // A2S_INFO 60 | if (QueryPacket.info == packet) { 61 | udpSocket.send(infoReply, datagram.address, datagram.port); 62 | return; 63 | } 64 | 65 | // A2S_PLAYERS 66 | if (header == 0x55) { 67 | if (bytes.length != 9) { 68 | return; 69 | } 70 | final challenge = bytes.sublist(5, 9); 71 | if (_eq.equals(challenge, _emptyPacket)) { 72 | udpSocket.send(challengeReply, datagram.address, datagram.port); 73 | return; 74 | } 75 | if (_eq.equals(challenge, _challengePacket)) { 76 | udpSocket.send(playersReply, datagram.address, datagram.port); 77 | return; 78 | } 79 | return; 80 | } 81 | 82 | // A2S_RULES 83 | if (header == 0x56) { 84 | if (bytes.length != 9) { 85 | return; 86 | } 87 | final challenge = bytes.sublist(5, 9); 88 | if (_eq.equals(challenge, _emptyPacket)) { 89 | udpSocket.send(challengeReply, datagram.address, datagram.port); 90 | return; 91 | } 92 | if (_eq.equals(challenge, _challengePacket)) { 93 | udpSocket.send(rulesReply, datagram.address, datagram.port); 94 | return; 95 | } 96 | return; 97 | } 98 | } 99 | 100 | late final List infoReply = _getInfoReply(); 101 | late final List playersReply = _getPlayersReply(); 102 | late final List rulesReply = _getRulesReply(); 103 | 104 | final List challengeReply = [..._emptyPacket, 0x41, ..._challengePacket]; 105 | 106 | List _getInfoReply() { 107 | final write = WriteBuffer(153, fixedSize: true); 108 | 109 | write.writeUint8(0xFF); 110 | write.writeUint8(0xFF); 111 | write.writeUint8(0xFF); 112 | write.writeUint8(0xFF); 113 | write.writeUint8(0x49); // Header 114 | 115 | write.writeUint8(17); // Protocol 116 | write.writeString('Dummy server! - Join here!'); // Name 117 | write.writeString('de_dust2'); // Map 118 | write.writeString('csgo'); // Folder 119 | write.writeString('Counter-Strike: Global Offensive'); // Game name 120 | write.writeUint16(730); // Game id 121 | write.writeUint8(16); // Players 122 | write.writeUint8(64); // Max Players 123 | write.writeUint8(5); // Bots 124 | write.writeUint8(ServerType.dedicated.code); // Type 125 | write.writeUint8(ServerOS.linux.code); // OS 126 | write.writeUint8(ServerVisibility.public.code); // Visibility 127 | write.writeUint8(ServerVAC.secured.code); // Vac 128 | write.writeString('1.37.9.4'); // Version 129 | write.writeUint8(0xF1); // Extra Data Flags 130 | 131 | write.writeUint16(27015); // Port 132 | write.writeInt64(10000000000000000); // Steamid 133 | write.writeUint16(27016); // TV port 134 | write.writeString('Source TV'); // TV name 135 | write.writeString('hello,world,nice,server'); // Keywords 136 | write.writeUint64(730); // Game64 id 137 | 138 | return write.data.buffer.asUint8List(); 139 | } 140 | 141 | List _getPlayersReply() { 142 | final write = WriteBuffer(0); 143 | const players = 16; 144 | 145 | write.writeUint8(0xFF); 146 | write.writeUint8(0xFF); 147 | write.writeUint8(0xFF); 148 | write.writeUint8(0xFF); 149 | write.writeUint8(0x44); // Header 150 | 151 | write.writeUint8(players); // Players 152 | for (var i = 0; i < players; i++) { 153 | write.writeUint8(0); // Index 154 | write.writeString('Player - $i'); // Name 155 | write.writeInt32(i * 3); // Score 156 | write.writeFloat32(i * 5); // Connection time 157 | } 158 | 159 | return write.data.buffer.asUint8List(); 160 | } 161 | 162 | List _getRulesReply() { 163 | final write = WriteBuffer(0); 164 | const rules = 8; 165 | 166 | write.writeUint8(0xFF); 167 | write.writeUint8(0xFF); 168 | write.writeUint8(0xFF); 169 | write.writeUint8(0xFF); 170 | write.writeUint8(0x45); // Header 171 | 172 | write.writeUint16(rules); // Rules 173 | for (var i = 0; i < rules; i++) { 174 | write.writeString('rule_$i'); // Name 175 | write.writeString('value_$i'); // Value 176 | } 177 | 178 | return write.data.buffer.asUint8List(); 179 | } 180 | 181 | /* TCP SECTION - RCON */ 182 | final Map authStatus = {}; 183 | 184 | void newSocket(Socket socket) { 185 | authStatus[socket.address] = false; 186 | 187 | socket.listen( 188 | (data) => onRconData(socket, data), 189 | onDone: () { 190 | authStatus.remove(socket.address); 191 | }, 192 | ); 193 | } 194 | 195 | Future onRconData(Socket socket, Uint8List data) async { 196 | if (data.length < 14) { 197 | return; 198 | } 199 | 200 | final packet = RconPacket(data); 201 | 202 | if (packet.size + 4 != data.length) { 203 | return; 204 | } 205 | 206 | // Auth 207 | if (packet.type == 3) { 208 | socket.add(RconPacket.from(id: packet.id).bytes); 209 | if (password.isNotEmpty && password == packet.bodyAsString) { 210 | // Workaround to avoid this and the previous packet to be added to the same packet. 211 | await Future( 212 | () => socket.add(RconPacket.from(id: packet.id, type: 2).bytes), 213 | ); 214 | authStatus[socket.address] = true; 215 | } else { 216 | await Future( 217 | () => socket.add(RconPacket.from(id: 0xFFFFFFFF, type: 2).bytes), 218 | ); 219 | } 220 | return; 221 | } 222 | 223 | if (packet.type == 2) { 224 | if (authStatus[socket.address] != true) { 225 | socket.add(RconPacket.from(id: packet.id).bytes); 226 | return; 227 | } 228 | 229 | final body = packet.bodyAsString; 230 | final args = body.split(RegExp(r'\s+')); 231 | 232 | // The only valid command 233 | if (args[0] != 'echo') { 234 | socket.add( 235 | RconPacket.from( 236 | id: packet.id, 237 | body: 'Unknown command "${args[0]}"\n', 238 | ).bytes, 239 | ); 240 | return; 241 | } 242 | 243 | socket.add( 244 | RconPacket.from( 245 | id: packet.id, 246 | body: '${args.sublist(1).join(' ')} \n', 247 | ).bytes, 248 | ); 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /lib/src/query/source/query_socket.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:collection/collection.dart'; 6 | import 'package:concurrent_queue/concurrent_queue.dart'; 7 | import 'package:logging/logging.dart'; 8 | 9 | import '../../../source_server.dart'; 10 | import '../../buffer.dart'; 11 | import 'query_packet.dart'; 12 | 13 | /// Wrapper for the query protocol. 14 | abstract class QuerySocket { 15 | factory QuerySocket._( 16 | InternetAddress address, 17 | int port, 18 | RawDatagramSocket socket, 19 | ) = _QuerySocketImpl; 20 | 21 | /// Returns the info about this server. 22 | Future getInfo(); 23 | 24 | /// Returns a list of currently connected players. 25 | Future> getPlayers(); 26 | 27 | /// Returns the server rules, or configuration variables in name/value pairs. 28 | /// Warning: In some games such as CS:GO this never completes without 29 | /// having installed a plugin on the server. 30 | Future> getRules(); 31 | 32 | /// Closes the connection 33 | void close(); 34 | 35 | /// Setup the connection to the remote server. 36 | /// This does not guarantee that the connection will be established successfully. 37 | static Future connect( 38 | dynamic address, 39 | int port, { 40 | int localPort = 0, 41 | }) async { 42 | assert(address is String || address is InternetAddress); 43 | if (address is String) { 44 | // ignore: parameter_assignments 45 | address = (await InternetAddress.lookup(address)).first; 46 | } 47 | 48 | final socket = 49 | await RawDatagramSocket.bind(InternetAddress.anyIPv4, localPort); 50 | return QuerySocket._(address as InternetAddress, port, socket); 51 | } 52 | } 53 | 54 | class _QuerySocketImpl implements QuerySocket { 55 | final InternetAddress address; 56 | final int port; 57 | final RawDatagramSocket socket; 58 | final ConcurrentQueue queue = ConcurrentQueue(concurrency: 1); 59 | 60 | Completer? infoCompleter; 61 | Completer>? playersCompleter; 62 | Completer>? rulesCompleter; 63 | 64 | final Logger _logger; 65 | 66 | _QuerySocketImpl(this.address, this.port, this.socket) 67 | : _logger = Logger('QuerySocket${address.address}:$port') { 68 | socket.listen(onEvent); 69 | } 70 | 71 | /// Requests challenge and gets 72 | void requestChallenge() { 73 | socket.send(QueryPacket.challenge.bytes, address, port); 74 | } 75 | 76 | @override 77 | Future getInfo() { 78 | return queue.add(() { 79 | assert(!(infoCompleter?.isCompleted ?? false)); 80 | if (infoCompleter != null) { 81 | return infoCompleter!.future; 82 | } 83 | infoCompleter = Completer(); 84 | _logger.finest('Packet: ${QueryPacket.info.bytes.printHex()}'); 85 | socket.send(QueryPacket.info.bytes, address, port); 86 | return infoCompleter!.future; 87 | }); 88 | } 89 | 90 | @override 91 | Future> getPlayers() async { 92 | return queue.add(() { 93 | assert(!(playersCompleter?.isCompleted ?? false)); 94 | if (playersCompleter != null) { 95 | return playersCompleter!.future; 96 | } 97 | playersCompleter = Completer>(); 98 | requestChallenge(); 99 | return playersCompleter!.future; 100 | }); 101 | } 102 | 103 | @override 104 | Future> getRules() async { 105 | return queue.add(() { 106 | assert(!(rulesCompleter?.isCompleted ?? false)); 107 | if (rulesCompleter != null) { 108 | return rulesCompleter!.future; 109 | } 110 | rulesCompleter = Completer>(); 111 | requestChallenge(); 112 | return rulesCompleter!.future; 113 | }); 114 | } 115 | 116 | void parseInfo(Uint8List bytes) { 117 | final read = ReadBuffer.fromUint8List(bytes); 118 | 119 | final protocol = read.uint8; 120 | final name = read.string; 121 | final map = read.string; 122 | final folder = read.string; 123 | final game = read.string; 124 | final id = read.uint16; 125 | final players = read.uint8; 126 | final maxPlayers = read.uint8; 127 | final bots = read.uint8; 128 | final type = ServerType(read.uint8); 129 | final os = ServerOS(read.uint8); 130 | final visibility = ServerVisibility(read.uint8); 131 | final vac = ServerVAC(read.uint8); 132 | /* TODO: Add TheShip flags*/ 133 | final version = read.string; 134 | var info = ServerInfo( 135 | protocol: protocol, 136 | name: name, 137 | map: map, 138 | folder: folder, 139 | game: game, 140 | id: id, 141 | players: players, 142 | maxPlayers: maxPlayers, 143 | bots: bots, 144 | type: type, 145 | os: os, 146 | visibility: visibility, 147 | vac: vac, 148 | version: version, 149 | ); 150 | if (read.canReadMore) { 151 | final edf = read.uint8; 152 | int? port; 153 | int? steamId; 154 | int? tvPort; 155 | String? tvName; 156 | String? keywords; 157 | int? gameId; 158 | 159 | if (edf & 0x80 != 0) { 160 | port = read.uint16; 161 | } 162 | if (edf & 0x10 != 0) { 163 | steamId = read.int64; 164 | } 165 | if (edf & 0x40 != 0) { 166 | tvPort = read.uint16; 167 | tvName = read.string; 168 | } 169 | if (edf & 0x20 != 0) { 170 | keywords = read.string; 171 | } 172 | if (edf & 0x01 != 0) { 173 | gameId = read.uint64; 174 | } 175 | info = info.copyWith( 176 | port: port, 177 | steamId: steamId, 178 | tvPort: tvPort, 179 | tvName: tvName, 180 | keywords: keywords, 181 | gameId: gameId, 182 | ); 183 | } 184 | 185 | infoCompleter!.complete(info); 186 | infoCompleter = null; 187 | } 188 | 189 | /// Parse the challenge and send the corresponding request. 190 | void parseChallenge(Uint8List bytes) { 191 | QueryPacket? packet; 192 | if (infoCompleter != null) { 193 | packet = QueryPacket.infoChallenge(bytes); 194 | } else if (playersCompleter != null) { 195 | packet = QueryPacket.players(bytes); 196 | } else if (rulesCompleter != null) { 197 | packet = QueryPacket.rules(bytes); 198 | } 199 | 200 | if (packet != null) { 201 | socket.send(packet.bytes, address, port); 202 | } else { 203 | _logger.severe('Requested challenge packet without any completer.'); 204 | } 205 | } 206 | 207 | void parsePlayers(Uint8List bytes) { 208 | assert(playersCompleter != null); 209 | assert(!playersCompleter!.isCompleted); 210 | final read = ReadBuffer.fromUint8List(bytes.sublist(1)); 211 | final players = []; 212 | while (read.canReadMore) { 213 | players.add( 214 | QueryPlayer( 215 | index: read.uint8, 216 | name: read.string, 217 | score: read.int32, 218 | duration: read.float32, 219 | ), 220 | ); 221 | /* TODO: Add TheShip params */ 222 | } 223 | playersCompleter!.complete(UnmodifiableListView(players)); 224 | playersCompleter = null; 225 | } 226 | 227 | void parseRules(Uint8List bytes) { 228 | assert(rulesCompleter != null); 229 | assert(!rulesCompleter!.isCompleted); 230 | final read = ReadBuffer.fromUint8List(bytes.sublist(2)); 231 | final rules = []; 232 | while (read.canReadMore) { 233 | rules.add(ServerRule(read.string, read.string)); 234 | } 235 | rulesCompleter!.complete(UnmodifiableListView(rules)); 236 | rulesCompleter = null; 237 | } 238 | 239 | void onEvent(RawSocketEvent event) { 240 | if (event != RawSocketEvent.read) { 241 | return; 242 | } 243 | final datagram = socket.receive(); 244 | if (datagram == null) { 245 | return; 246 | } 247 | 248 | final data = datagram.data; 249 | final header = data[4]; 250 | 251 | _logger.finest('Packet: ${data.printHex()}'); 252 | 253 | if (header == 0x49) { 254 | parseInfo(data.sublist(5)); 255 | } else if (header == 0x41) { 256 | parseChallenge(data.sublist(5)); 257 | } else if (header == 0x44) { 258 | parsePlayers(data.sublist(5)); 259 | } else if (header == 0x45) { 260 | parseRules(data.sublist(5)); 261 | } else { 262 | throw QueryException('Unrecognized header: $header'); 263 | } 264 | } 265 | 266 | @override 267 | void close() => socket.close(); 268 | } 269 | 270 | extension on List { 271 | String printHex() => '[${map((e) => e.toRadixString(16)).join(',')}]'; 272 | } 273 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/models/server_info.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'server_info.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$ServerInfo { 19 | /// MOTD. 20 | String get motd => throw _privateConstructorUsedError; 21 | 22 | /// Gametype. 23 | String get gametype => throw _privateConstructorUsedError; 24 | 25 | /// Map. 26 | String get map => throw _privateConstructorUsedError; 27 | 28 | /// Number of players on the server. 29 | int get players => throw _privateConstructorUsedError; 30 | 31 | /// Maximum number of players the server reports it can hold. 32 | int get maxPlayers => throw _privateConstructorUsedError; 33 | 34 | /// Server's port. 35 | int get port => throw _privateConstructorUsedError; 36 | 37 | /// Server's name. 38 | String get ip => throw _privateConstructorUsedError; 39 | 40 | @JsonKey(ignore: true) 41 | $ServerInfoCopyWith get copyWith => 42 | throw _privateConstructorUsedError; 43 | } 44 | 45 | /// @nodoc 46 | abstract class $ServerInfoCopyWith<$Res> { 47 | factory $ServerInfoCopyWith( 48 | ServerInfo value, $Res Function(ServerInfo) then) = 49 | _$ServerInfoCopyWithImpl<$Res>; 50 | $Res call( 51 | {String motd, 52 | String gametype, 53 | String map, 54 | int players, 55 | int maxPlayers, 56 | int port, 57 | String ip}); 58 | } 59 | 60 | /// @nodoc 61 | class _$ServerInfoCopyWithImpl<$Res> implements $ServerInfoCopyWith<$Res> { 62 | _$ServerInfoCopyWithImpl(this._value, this._then); 63 | 64 | final ServerInfo _value; 65 | // ignore: unused_field 66 | final $Res Function(ServerInfo) _then; 67 | 68 | @override 69 | $Res call({ 70 | Object? motd = freezed, 71 | Object? gametype = freezed, 72 | Object? map = freezed, 73 | Object? players = freezed, 74 | Object? maxPlayers = freezed, 75 | Object? port = freezed, 76 | Object? ip = freezed, 77 | }) { 78 | return _then(_value.copyWith( 79 | motd: motd == freezed 80 | ? _value.motd 81 | : motd // ignore: cast_nullable_to_non_nullable 82 | as String, 83 | gametype: gametype == freezed 84 | ? _value.gametype 85 | : gametype // ignore: cast_nullable_to_non_nullable 86 | as String, 87 | map: map == freezed 88 | ? _value.map 89 | : map // ignore: cast_nullable_to_non_nullable 90 | as String, 91 | players: players == freezed 92 | ? _value.players 93 | : players // ignore: cast_nullable_to_non_nullable 94 | as int, 95 | maxPlayers: maxPlayers == freezed 96 | ? _value.maxPlayers 97 | : maxPlayers // ignore: cast_nullable_to_non_nullable 98 | as int, 99 | port: port == freezed 100 | ? _value.port 101 | : port // ignore: cast_nullable_to_non_nullable 102 | as int, 103 | ip: ip == freezed 104 | ? _value.ip 105 | : ip // ignore: cast_nullable_to_non_nullable 106 | as String, 107 | )); 108 | } 109 | } 110 | 111 | /// @nodoc 112 | abstract class _$$_ServerInfoCopyWith<$Res> 113 | implements $ServerInfoCopyWith<$Res> { 114 | factory _$$_ServerInfoCopyWith( 115 | _$_ServerInfo value, $Res Function(_$_ServerInfo) then) = 116 | __$$_ServerInfoCopyWithImpl<$Res>; 117 | @override 118 | $Res call( 119 | {String motd, 120 | String gametype, 121 | String map, 122 | int players, 123 | int maxPlayers, 124 | int port, 125 | String ip}); 126 | } 127 | 128 | /// @nodoc 129 | class __$$_ServerInfoCopyWithImpl<$Res> extends _$ServerInfoCopyWithImpl<$Res> 130 | implements _$$_ServerInfoCopyWith<$Res> { 131 | __$$_ServerInfoCopyWithImpl( 132 | _$_ServerInfo _value, $Res Function(_$_ServerInfo) _then) 133 | : super(_value, (v) => _then(v as _$_ServerInfo)); 134 | 135 | @override 136 | _$_ServerInfo get _value => super._value as _$_ServerInfo; 137 | 138 | @override 139 | $Res call({ 140 | Object? motd = freezed, 141 | Object? gametype = freezed, 142 | Object? map = freezed, 143 | Object? players = freezed, 144 | Object? maxPlayers = freezed, 145 | Object? port = freezed, 146 | Object? ip = freezed, 147 | }) { 148 | return _then(_$_ServerInfo( 149 | motd: motd == freezed 150 | ? _value.motd 151 | : motd // ignore: cast_nullable_to_non_nullable 152 | as String, 153 | gametype: gametype == freezed 154 | ? _value.gametype 155 | : gametype // ignore: cast_nullable_to_non_nullable 156 | as String, 157 | map: map == freezed 158 | ? _value.map 159 | : map // ignore: cast_nullable_to_non_nullable 160 | as String, 161 | players: players == freezed 162 | ? _value.players 163 | : players // ignore: cast_nullable_to_non_nullable 164 | as int, 165 | maxPlayers: maxPlayers == freezed 166 | ? _value.maxPlayers 167 | : maxPlayers // ignore: cast_nullable_to_non_nullable 168 | as int, 169 | port: port == freezed 170 | ? _value.port 171 | : port // ignore: cast_nullable_to_non_nullable 172 | as int, 173 | ip: ip == freezed 174 | ? _value.ip 175 | : ip // ignore: cast_nullable_to_non_nullable 176 | as String, 177 | )); 178 | } 179 | } 180 | 181 | /// @nodoc 182 | 183 | class _$_ServerInfo implements _ServerInfo { 184 | _$_ServerInfo( 185 | {required this.motd, 186 | required this.gametype, 187 | required this.map, 188 | required this.players, 189 | required this.maxPlayers, 190 | required this.port, 191 | required this.ip}); 192 | 193 | /// MOTD. 194 | @override 195 | final String motd; 196 | 197 | /// Gametype. 198 | @override 199 | final String gametype; 200 | 201 | /// Map. 202 | @override 203 | final String map; 204 | 205 | /// Number of players on the server. 206 | @override 207 | final int players; 208 | 209 | /// Maximum number of players the server reports it can hold. 210 | @override 211 | final int maxPlayers; 212 | 213 | /// Server's port. 214 | @override 215 | final int port; 216 | 217 | /// Server's name. 218 | @override 219 | final String ip; 220 | 221 | @override 222 | String toString() { 223 | return 'ServerInfo(motd: $motd, gametype: $gametype, map: $map, players: $players, maxPlayers: $maxPlayers, port: $port, ip: $ip)'; 224 | } 225 | 226 | @override 227 | bool operator ==(dynamic other) { 228 | return identical(this, other) || 229 | (other.runtimeType == runtimeType && 230 | other is _$_ServerInfo && 231 | const DeepCollectionEquality().equals(other.motd, motd) && 232 | const DeepCollectionEquality().equals(other.gametype, gametype) && 233 | const DeepCollectionEquality().equals(other.map, map) && 234 | const DeepCollectionEquality().equals(other.players, players) && 235 | const DeepCollectionEquality() 236 | .equals(other.maxPlayers, maxPlayers) && 237 | const DeepCollectionEquality().equals(other.port, port) && 238 | const DeepCollectionEquality().equals(other.ip, ip)); 239 | } 240 | 241 | @override 242 | int get hashCode => Object.hash( 243 | runtimeType, 244 | const DeepCollectionEquality().hash(motd), 245 | const DeepCollectionEquality().hash(gametype), 246 | const DeepCollectionEquality().hash(map), 247 | const DeepCollectionEquality().hash(players), 248 | const DeepCollectionEquality().hash(maxPlayers), 249 | const DeepCollectionEquality().hash(port), 250 | const DeepCollectionEquality().hash(ip)); 251 | 252 | @JsonKey(ignore: true) 253 | @override 254 | _$$_ServerInfoCopyWith<_$_ServerInfo> get copyWith => 255 | __$$_ServerInfoCopyWithImpl<_$_ServerInfo>(this, _$identity); 256 | } 257 | 258 | abstract class _ServerInfo implements ServerInfo { 259 | factory _ServerInfo( 260 | {required final String motd, 261 | required final String gametype, 262 | required final String map, 263 | required final int players, 264 | required final int maxPlayers, 265 | required final int port, 266 | required final String ip}) = _$_ServerInfo; 267 | 268 | @override 269 | 270 | /// MOTD. 271 | String get motd => throw _privateConstructorUsedError; 272 | @override 273 | 274 | /// Gametype. 275 | String get gametype => throw _privateConstructorUsedError; 276 | @override 277 | 278 | /// Map. 279 | String get map => throw _privateConstructorUsedError; 280 | @override 281 | 282 | /// Number of players on the server. 283 | int get players => throw _privateConstructorUsedError; 284 | @override 285 | 286 | /// Maximum number of players the server reports it can hold. 287 | int get maxPlayers => throw _privateConstructorUsedError; 288 | @override 289 | 290 | /// Server's port. 291 | int get port => throw _privateConstructorUsedError; 292 | @override 293 | 294 | /// Server's name. 295 | String get ip => throw _privateConstructorUsedError; 296 | @override 297 | @JsonKey(ignore: true) 298 | _$$_ServerInfoCopyWith<_$_ServerInfo> get copyWith => 299 | throw _privateConstructorUsedError; 300 | } 301 | -------------------------------------------------------------------------------- /lib/src/query/source/models/server_info.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'server_info.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$ServerInfo { 19 | /// Protocol version used by the server. 20 | int get protocol => throw _privateConstructorUsedError; 21 | 22 | /// Name of the server. 23 | String get name => throw _privateConstructorUsedError; 24 | 25 | /// Map the server has currently loaded. 26 | String get map => throw _privateConstructorUsedError; 27 | 28 | /// Name of the folder containing the game files. 29 | String get folder => throw _privateConstructorUsedError; 30 | 31 | /// Full name of the game. 32 | String get game => throw _privateConstructorUsedError; 33 | 34 | /// Steam Application ID of game. 35 | int get id => throw _privateConstructorUsedError; 36 | 37 | /// Number of players on the server. 38 | int get players => throw _privateConstructorUsedError; 39 | 40 | /// Maximum number of players the server reports it can hold. 41 | int get maxPlayers => throw _privateConstructorUsedError; 42 | 43 | /// Number of bots on the server. 44 | int get bots => throw _privateConstructorUsedError; 45 | 46 | /// Server type. 47 | ServerType get type => throw _privateConstructorUsedError; 48 | 49 | /// Server operating system. 50 | ServerOS get os => throw _privateConstructorUsedError; 51 | 52 | /// Server visibility. Indicates if the server is password protected. 53 | ServerVisibility get visibility => throw _privateConstructorUsedError; 54 | 55 | /// Server VAC status. 56 | ServerVAC get vac => 57 | throw _privateConstructorUsedError; /* TODO: Add TheShip flags */ 58 | /// Version of the game installed on the server. 59 | String get version => throw _privateConstructorUsedError; 60 | 61 | /// Server's port number. 62 | int? get port => throw _privateConstructorUsedError; 63 | 64 | /// Server's SteamID. 65 | int? get steamId => throw _privateConstructorUsedError; 66 | 67 | /// Spectator port number for SourceTV. 68 | int? get tvPort => throw _privateConstructorUsedError; 69 | 70 | /// Name of the spectator server for SourceTV. 71 | String? get tvName => throw _privateConstructorUsedError; 72 | 73 | /// Tags that describe the game according to the server (for future use.) 74 | String? get keywords => throw _privateConstructorUsedError; 75 | 76 | /// The server's 64-bit GameID. 77 | int? get gameId => throw _privateConstructorUsedError; 78 | 79 | @JsonKey(ignore: true) 80 | $ServerInfoCopyWith get copyWith => 81 | throw _privateConstructorUsedError; 82 | } 83 | 84 | /// @nodoc 85 | abstract class $ServerInfoCopyWith<$Res> { 86 | factory $ServerInfoCopyWith( 87 | ServerInfo value, $Res Function(ServerInfo) then) = 88 | _$ServerInfoCopyWithImpl<$Res>; 89 | $Res call( 90 | {int protocol, 91 | String name, 92 | String map, 93 | String folder, 94 | String game, 95 | int id, 96 | int players, 97 | int maxPlayers, 98 | int bots, 99 | ServerType type, 100 | ServerOS os, 101 | ServerVisibility visibility, 102 | ServerVAC vac, 103 | String version, 104 | int? port, 105 | int? steamId, 106 | int? tvPort, 107 | String? tvName, 108 | String? keywords, 109 | int? gameId}); 110 | } 111 | 112 | /// @nodoc 113 | class _$ServerInfoCopyWithImpl<$Res> implements $ServerInfoCopyWith<$Res> { 114 | _$ServerInfoCopyWithImpl(this._value, this._then); 115 | 116 | final ServerInfo _value; 117 | // ignore: unused_field 118 | final $Res Function(ServerInfo) _then; 119 | 120 | @override 121 | $Res call({ 122 | Object? protocol = freezed, 123 | Object? name = freezed, 124 | Object? map = freezed, 125 | Object? folder = freezed, 126 | Object? game = freezed, 127 | Object? id = freezed, 128 | Object? players = freezed, 129 | Object? maxPlayers = freezed, 130 | Object? bots = freezed, 131 | Object? type = freezed, 132 | Object? os = freezed, 133 | Object? visibility = freezed, 134 | Object? vac = freezed, 135 | Object? version = freezed, 136 | Object? port = freezed, 137 | Object? steamId = freezed, 138 | Object? tvPort = freezed, 139 | Object? tvName = freezed, 140 | Object? keywords = freezed, 141 | Object? gameId = freezed, 142 | }) { 143 | return _then(_value.copyWith( 144 | protocol: protocol == freezed 145 | ? _value.protocol 146 | : protocol // ignore: cast_nullable_to_non_nullable 147 | as int, 148 | name: name == freezed 149 | ? _value.name 150 | : name // ignore: cast_nullable_to_non_nullable 151 | as String, 152 | map: map == freezed 153 | ? _value.map 154 | : map // ignore: cast_nullable_to_non_nullable 155 | as String, 156 | folder: folder == freezed 157 | ? _value.folder 158 | : folder // ignore: cast_nullable_to_non_nullable 159 | as String, 160 | game: game == freezed 161 | ? _value.game 162 | : game // ignore: cast_nullable_to_non_nullable 163 | as String, 164 | id: id == freezed 165 | ? _value.id 166 | : id // ignore: cast_nullable_to_non_nullable 167 | as int, 168 | players: players == freezed 169 | ? _value.players 170 | : players // ignore: cast_nullable_to_non_nullable 171 | as int, 172 | maxPlayers: maxPlayers == freezed 173 | ? _value.maxPlayers 174 | : maxPlayers // ignore: cast_nullable_to_non_nullable 175 | as int, 176 | bots: bots == freezed 177 | ? _value.bots 178 | : bots // ignore: cast_nullable_to_non_nullable 179 | as int, 180 | type: type == freezed 181 | ? _value.type 182 | : type // ignore: cast_nullable_to_non_nullable 183 | as ServerType, 184 | os: os == freezed 185 | ? _value.os 186 | : os // ignore: cast_nullable_to_non_nullable 187 | as ServerOS, 188 | visibility: visibility == freezed 189 | ? _value.visibility 190 | : visibility // ignore: cast_nullable_to_non_nullable 191 | as ServerVisibility, 192 | vac: vac == freezed 193 | ? _value.vac 194 | : vac // ignore: cast_nullable_to_non_nullable 195 | as ServerVAC, 196 | version: version == freezed 197 | ? _value.version 198 | : version // ignore: cast_nullable_to_non_nullable 199 | as String, 200 | port: port == freezed 201 | ? _value.port 202 | : port // ignore: cast_nullable_to_non_nullable 203 | as int?, 204 | steamId: steamId == freezed 205 | ? _value.steamId 206 | : steamId // ignore: cast_nullable_to_non_nullable 207 | as int?, 208 | tvPort: tvPort == freezed 209 | ? _value.tvPort 210 | : tvPort // ignore: cast_nullable_to_non_nullable 211 | as int?, 212 | tvName: tvName == freezed 213 | ? _value.tvName 214 | : tvName // ignore: cast_nullable_to_non_nullable 215 | as String?, 216 | keywords: keywords == freezed 217 | ? _value.keywords 218 | : keywords // ignore: cast_nullable_to_non_nullable 219 | as String?, 220 | gameId: gameId == freezed 221 | ? _value.gameId 222 | : gameId // ignore: cast_nullable_to_non_nullable 223 | as int?, 224 | )); 225 | } 226 | } 227 | 228 | /// @nodoc 229 | abstract class _$$_ServerInfoCopyWith<$Res> 230 | implements $ServerInfoCopyWith<$Res> { 231 | factory _$$_ServerInfoCopyWith( 232 | _$_ServerInfo value, $Res Function(_$_ServerInfo) then) = 233 | __$$_ServerInfoCopyWithImpl<$Res>; 234 | @override 235 | $Res call( 236 | {int protocol, 237 | String name, 238 | String map, 239 | String folder, 240 | String game, 241 | int id, 242 | int players, 243 | int maxPlayers, 244 | int bots, 245 | ServerType type, 246 | ServerOS os, 247 | ServerVisibility visibility, 248 | ServerVAC vac, 249 | String version, 250 | int? port, 251 | int? steamId, 252 | int? tvPort, 253 | String? tvName, 254 | String? keywords, 255 | int? gameId}); 256 | } 257 | 258 | /// @nodoc 259 | class __$$_ServerInfoCopyWithImpl<$Res> extends _$ServerInfoCopyWithImpl<$Res> 260 | implements _$$_ServerInfoCopyWith<$Res> { 261 | __$$_ServerInfoCopyWithImpl( 262 | _$_ServerInfo _value, $Res Function(_$_ServerInfo) _then) 263 | : super(_value, (v) => _then(v as _$_ServerInfo)); 264 | 265 | @override 266 | _$_ServerInfo get _value => super._value as _$_ServerInfo; 267 | 268 | @override 269 | $Res call({ 270 | Object? protocol = freezed, 271 | Object? name = freezed, 272 | Object? map = freezed, 273 | Object? folder = freezed, 274 | Object? game = freezed, 275 | Object? id = freezed, 276 | Object? players = freezed, 277 | Object? maxPlayers = freezed, 278 | Object? bots = freezed, 279 | Object? type = freezed, 280 | Object? os = freezed, 281 | Object? visibility = freezed, 282 | Object? vac = freezed, 283 | Object? version = freezed, 284 | Object? port = freezed, 285 | Object? steamId = freezed, 286 | Object? tvPort = freezed, 287 | Object? tvName = freezed, 288 | Object? keywords = freezed, 289 | Object? gameId = freezed, 290 | }) { 291 | return _then(_$_ServerInfo( 292 | protocol: protocol == freezed 293 | ? _value.protocol 294 | : protocol // ignore: cast_nullable_to_non_nullable 295 | as int, 296 | name: name == freezed 297 | ? _value.name 298 | : name // ignore: cast_nullable_to_non_nullable 299 | as String, 300 | map: map == freezed 301 | ? _value.map 302 | : map // ignore: cast_nullable_to_non_nullable 303 | as String, 304 | folder: folder == freezed 305 | ? _value.folder 306 | : folder // ignore: cast_nullable_to_non_nullable 307 | as String, 308 | game: game == freezed 309 | ? _value.game 310 | : game // ignore: cast_nullable_to_non_nullable 311 | as String, 312 | id: id == freezed 313 | ? _value.id 314 | : id // ignore: cast_nullable_to_non_nullable 315 | as int, 316 | players: players == freezed 317 | ? _value.players 318 | : players // ignore: cast_nullable_to_non_nullable 319 | as int, 320 | maxPlayers: maxPlayers == freezed 321 | ? _value.maxPlayers 322 | : maxPlayers // ignore: cast_nullable_to_non_nullable 323 | as int, 324 | bots: bots == freezed 325 | ? _value.bots 326 | : bots // ignore: cast_nullable_to_non_nullable 327 | as int, 328 | type: type == freezed 329 | ? _value.type 330 | : type // ignore: cast_nullable_to_non_nullable 331 | as ServerType, 332 | os: os == freezed 333 | ? _value.os 334 | : os // ignore: cast_nullable_to_non_nullable 335 | as ServerOS, 336 | visibility: visibility == freezed 337 | ? _value.visibility 338 | : visibility // ignore: cast_nullable_to_non_nullable 339 | as ServerVisibility, 340 | vac: vac == freezed 341 | ? _value.vac 342 | : vac // ignore: cast_nullable_to_non_nullable 343 | as ServerVAC, 344 | version: version == freezed 345 | ? _value.version 346 | : version // ignore: cast_nullable_to_non_nullable 347 | as String, 348 | port: port == freezed 349 | ? _value.port 350 | : port // ignore: cast_nullable_to_non_nullable 351 | as int?, 352 | steamId: steamId == freezed 353 | ? _value.steamId 354 | : steamId // ignore: cast_nullable_to_non_nullable 355 | as int?, 356 | tvPort: tvPort == freezed 357 | ? _value.tvPort 358 | : tvPort // ignore: cast_nullable_to_non_nullable 359 | as int?, 360 | tvName: tvName == freezed 361 | ? _value.tvName 362 | : tvName // ignore: cast_nullable_to_non_nullable 363 | as String?, 364 | keywords: keywords == freezed 365 | ? _value.keywords 366 | : keywords // ignore: cast_nullable_to_non_nullable 367 | as String?, 368 | gameId: gameId == freezed 369 | ? _value.gameId 370 | : gameId // ignore: cast_nullable_to_non_nullable 371 | as int?, 372 | )); 373 | } 374 | } 375 | 376 | /// @nodoc 377 | 378 | class _$_ServerInfo implements _ServerInfo { 379 | _$_ServerInfo( 380 | {required this.protocol, 381 | required this.name, 382 | required this.map, 383 | required this.folder, 384 | required this.game, 385 | required this.id, 386 | required this.players, 387 | required this.maxPlayers, 388 | required this.bots, 389 | required this.type, 390 | required this.os, 391 | required this.visibility, 392 | required this.vac, 393 | required this.version, 394 | this.port, 395 | this.steamId, 396 | this.tvPort, 397 | this.tvName, 398 | this.keywords, 399 | this.gameId}); 400 | 401 | /// Protocol version used by the server. 402 | @override 403 | final int protocol; 404 | 405 | /// Name of the server. 406 | @override 407 | final String name; 408 | 409 | /// Map the server has currently loaded. 410 | @override 411 | final String map; 412 | 413 | /// Name of the folder containing the game files. 414 | @override 415 | final String folder; 416 | 417 | /// Full name of the game. 418 | @override 419 | final String game; 420 | 421 | /// Steam Application ID of game. 422 | @override 423 | final int id; 424 | 425 | /// Number of players on the server. 426 | @override 427 | final int players; 428 | 429 | /// Maximum number of players the server reports it can hold. 430 | @override 431 | final int maxPlayers; 432 | 433 | /// Number of bots on the server. 434 | @override 435 | final int bots; 436 | 437 | /// Server type. 438 | @override 439 | final ServerType type; 440 | 441 | /// Server operating system. 442 | @override 443 | final ServerOS os; 444 | 445 | /// Server visibility. Indicates if the server is password protected. 446 | @override 447 | final ServerVisibility visibility; 448 | 449 | /// Server VAC status. 450 | @override 451 | final ServerVAC vac; 452 | /* TODO: Add TheShip flags */ 453 | /// Version of the game installed on the server. 454 | @override 455 | final String version; 456 | 457 | /// Server's port number. 458 | @override 459 | final int? port; 460 | 461 | /// Server's SteamID. 462 | @override 463 | final int? steamId; 464 | 465 | /// Spectator port number for SourceTV. 466 | @override 467 | final int? tvPort; 468 | 469 | /// Name of the spectator server for SourceTV. 470 | @override 471 | final String? tvName; 472 | 473 | /// Tags that describe the game according to the server (for future use.) 474 | @override 475 | final String? keywords; 476 | 477 | /// The server's 64-bit GameID. 478 | @override 479 | final int? gameId; 480 | 481 | @override 482 | String toString() { 483 | return 'ServerInfo(protocol: $protocol, name: $name, map: $map, folder: $folder, game: $game, id: $id, players: $players, maxPlayers: $maxPlayers, bots: $bots, type: $type, os: $os, visibility: $visibility, vac: $vac, version: $version, port: $port, steamId: $steamId, tvPort: $tvPort, tvName: $tvName, keywords: $keywords, gameId: $gameId)'; 484 | } 485 | 486 | @override 487 | bool operator ==(dynamic other) { 488 | return identical(this, other) || 489 | (other.runtimeType == runtimeType && 490 | other is _$_ServerInfo && 491 | const DeepCollectionEquality().equals(other.protocol, protocol) && 492 | const DeepCollectionEquality().equals(other.name, name) && 493 | const DeepCollectionEquality().equals(other.map, map) && 494 | const DeepCollectionEquality().equals(other.folder, folder) && 495 | const DeepCollectionEquality().equals(other.game, game) && 496 | const DeepCollectionEquality().equals(other.id, id) && 497 | const DeepCollectionEquality().equals(other.players, players) && 498 | const DeepCollectionEquality() 499 | .equals(other.maxPlayers, maxPlayers) && 500 | const DeepCollectionEquality().equals(other.bots, bots) && 501 | const DeepCollectionEquality().equals(other.type, type) && 502 | const DeepCollectionEquality().equals(other.os, os) && 503 | const DeepCollectionEquality() 504 | .equals(other.visibility, visibility) && 505 | const DeepCollectionEquality().equals(other.vac, vac) && 506 | const DeepCollectionEquality().equals(other.version, version) && 507 | const DeepCollectionEquality().equals(other.port, port) && 508 | const DeepCollectionEquality().equals(other.steamId, steamId) && 509 | const DeepCollectionEquality().equals(other.tvPort, tvPort) && 510 | const DeepCollectionEquality().equals(other.tvName, tvName) && 511 | const DeepCollectionEquality().equals(other.keywords, keywords) && 512 | const DeepCollectionEquality().equals(other.gameId, gameId)); 513 | } 514 | 515 | @override 516 | int get hashCode => Object.hashAll([ 517 | runtimeType, 518 | const DeepCollectionEquality().hash(protocol), 519 | const DeepCollectionEquality().hash(name), 520 | const DeepCollectionEquality().hash(map), 521 | const DeepCollectionEquality().hash(folder), 522 | const DeepCollectionEquality().hash(game), 523 | const DeepCollectionEquality().hash(id), 524 | const DeepCollectionEquality().hash(players), 525 | const DeepCollectionEquality().hash(maxPlayers), 526 | const DeepCollectionEquality().hash(bots), 527 | const DeepCollectionEquality().hash(type), 528 | const DeepCollectionEquality().hash(os), 529 | const DeepCollectionEquality().hash(visibility), 530 | const DeepCollectionEquality().hash(vac), 531 | const DeepCollectionEquality().hash(version), 532 | const DeepCollectionEquality().hash(port), 533 | const DeepCollectionEquality().hash(steamId), 534 | const DeepCollectionEquality().hash(tvPort), 535 | const DeepCollectionEquality().hash(tvName), 536 | const DeepCollectionEquality().hash(keywords), 537 | const DeepCollectionEquality().hash(gameId) 538 | ]); 539 | 540 | @JsonKey(ignore: true) 541 | @override 542 | _$$_ServerInfoCopyWith<_$_ServerInfo> get copyWith => 543 | __$$_ServerInfoCopyWithImpl<_$_ServerInfo>(this, _$identity); 544 | } 545 | 546 | abstract class _ServerInfo implements ServerInfo { 547 | factory _ServerInfo( 548 | {required final int protocol, 549 | required final String name, 550 | required final String map, 551 | required final String folder, 552 | required final String game, 553 | required final int id, 554 | required final int players, 555 | required final int maxPlayers, 556 | required final int bots, 557 | required final ServerType type, 558 | required final ServerOS os, 559 | required final ServerVisibility visibility, 560 | required final ServerVAC vac, 561 | required final String version, 562 | final int? port, 563 | final int? steamId, 564 | final int? tvPort, 565 | final String? tvName, 566 | final String? keywords, 567 | final int? gameId}) = _$_ServerInfo; 568 | 569 | @override 570 | 571 | /// Protocol version used by the server. 572 | int get protocol => throw _privateConstructorUsedError; 573 | @override 574 | 575 | /// Name of the server. 576 | String get name => throw _privateConstructorUsedError; 577 | @override 578 | 579 | /// Map the server has currently loaded. 580 | String get map => throw _privateConstructorUsedError; 581 | @override 582 | 583 | /// Name of the folder containing the game files. 584 | String get folder => throw _privateConstructorUsedError; 585 | @override 586 | 587 | /// Full name of the game. 588 | String get game => throw _privateConstructorUsedError; 589 | @override 590 | 591 | /// Steam Application ID of game. 592 | int get id => throw _privateConstructorUsedError; 593 | @override 594 | 595 | /// Number of players on the server. 596 | int get players => throw _privateConstructorUsedError; 597 | @override 598 | 599 | /// Maximum number of players the server reports it can hold. 600 | int get maxPlayers => throw _privateConstructorUsedError; 601 | @override 602 | 603 | /// Number of bots on the server. 604 | int get bots => throw _privateConstructorUsedError; 605 | @override 606 | 607 | /// Server type. 608 | ServerType get type => throw _privateConstructorUsedError; 609 | @override 610 | 611 | /// Server operating system. 612 | ServerOS get os => throw _privateConstructorUsedError; 613 | @override 614 | 615 | /// Server visibility. Indicates if the server is password protected. 616 | ServerVisibility get visibility => throw _privateConstructorUsedError; 617 | @override 618 | 619 | /// Server VAC status. 620 | ServerVAC get vac => throw _privateConstructorUsedError; 621 | @override /* TODO: Add TheShip flags */ 622 | /// Version of the game installed on the server. 623 | String get version => throw _privateConstructorUsedError; 624 | @override 625 | 626 | /// Server's port number. 627 | int? get port => throw _privateConstructorUsedError; 628 | @override 629 | 630 | /// Server's SteamID. 631 | int? get steamId => throw _privateConstructorUsedError; 632 | @override 633 | 634 | /// Spectator port number for SourceTV. 635 | int? get tvPort => throw _privateConstructorUsedError; 636 | @override 637 | 638 | /// Name of the spectator server for SourceTV. 639 | String? get tvName => throw _privateConstructorUsedError; 640 | @override 641 | 642 | /// Tags that describe the game according to the server (for future use.) 643 | String? get keywords => throw _privateConstructorUsedError; 644 | @override 645 | 646 | /// The server's 64-bit GameID. 647 | int? get gameId => throw _privateConstructorUsedError; 648 | @override 649 | @JsonKey(ignore: true) 650 | _$$_ServerInfoCopyWith<_$_ServerInfo> get copyWith => 651 | throw _privateConstructorUsedError; 652 | } 653 | -------------------------------------------------------------------------------- /lib/src/query/minecraft/models/server_ping_info.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'server_ping_info.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | ServerPingInfo _$ServerPingInfoFromJson(Map json) { 18 | return _ServerPingInfo.fromJson(json); 19 | } 20 | 21 | /// @nodoc 22 | mixin _$ServerPingInfo { 23 | /// Server and protocol version. 24 | Version get version => throw _privateConstructorUsedError; 25 | 26 | /// Total and connected players. 27 | Players get players => throw _privateConstructorUsedError; 28 | 29 | /// MOTD description. 30 | // ignore: invalid_annotation_target 31 | @JsonKey(fromJson: _descriptionToJson) 32 | Description get description => throw _privateConstructorUsedError; 33 | 34 | /// Favicon data uri. 35 | String? get favicon => throw _privateConstructorUsedError; 36 | 37 | /// Server's mods. 38 | Modinfo? get modinfo => throw _privateConstructorUsedError; 39 | 40 | /// Server's ping. 41 | int? get ping => throw _privateConstructorUsedError; 42 | 43 | Map toJson() => throw _privateConstructorUsedError; 44 | @JsonKey(ignore: true) 45 | $ServerPingInfoCopyWith get copyWith => 46 | throw _privateConstructorUsedError; 47 | } 48 | 49 | /// @nodoc 50 | abstract class $ServerPingInfoCopyWith<$Res> { 51 | factory $ServerPingInfoCopyWith( 52 | ServerPingInfo value, $Res Function(ServerPingInfo) then) = 53 | _$ServerPingInfoCopyWithImpl<$Res>; 54 | $Res call( 55 | {Version version, 56 | Players players, 57 | @JsonKey(fromJson: _descriptionToJson) Description description, 58 | String? favicon, 59 | Modinfo? modinfo, 60 | int? ping}); 61 | 62 | $VersionCopyWith<$Res> get version; 63 | $PlayersCopyWith<$Res> get players; 64 | $DescriptionCopyWith<$Res> get description; 65 | $ModinfoCopyWith<$Res>? get modinfo; 66 | } 67 | 68 | /// @nodoc 69 | class _$ServerPingInfoCopyWithImpl<$Res> 70 | implements $ServerPingInfoCopyWith<$Res> { 71 | _$ServerPingInfoCopyWithImpl(this._value, this._then); 72 | 73 | final ServerPingInfo _value; 74 | // ignore: unused_field 75 | final $Res Function(ServerPingInfo) _then; 76 | 77 | @override 78 | $Res call({ 79 | Object? version = freezed, 80 | Object? players = freezed, 81 | Object? description = freezed, 82 | Object? favicon = freezed, 83 | Object? modinfo = freezed, 84 | Object? ping = freezed, 85 | }) { 86 | return _then(_value.copyWith( 87 | version: version == freezed 88 | ? _value.version 89 | : version // ignore: cast_nullable_to_non_nullable 90 | as Version, 91 | players: players == freezed 92 | ? _value.players 93 | : players // ignore: cast_nullable_to_non_nullable 94 | as Players, 95 | description: description == freezed 96 | ? _value.description 97 | : description // ignore: cast_nullable_to_non_nullable 98 | as Description, 99 | favicon: favicon == freezed 100 | ? _value.favicon 101 | : favicon // ignore: cast_nullable_to_non_nullable 102 | as String?, 103 | modinfo: modinfo == freezed 104 | ? _value.modinfo 105 | : modinfo // ignore: cast_nullable_to_non_nullable 106 | as Modinfo?, 107 | ping: ping == freezed 108 | ? _value.ping 109 | : ping // ignore: cast_nullable_to_non_nullable 110 | as int?, 111 | )); 112 | } 113 | 114 | @override 115 | $VersionCopyWith<$Res> get version { 116 | return $VersionCopyWith<$Res>(_value.version, (value) { 117 | return _then(_value.copyWith(version: value)); 118 | }); 119 | } 120 | 121 | @override 122 | $PlayersCopyWith<$Res> get players { 123 | return $PlayersCopyWith<$Res>(_value.players, (value) { 124 | return _then(_value.copyWith(players: value)); 125 | }); 126 | } 127 | 128 | @override 129 | $DescriptionCopyWith<$Res> get description { 130 | return $DescriptionCopyWith<$Res>(_value.description, (value) { 131 | return _then(_value.copyWith(description: value)); 132 | }); 133 | } 134 | 135 | @override 136 | $ModinfoCopyWith<$Res>? get modinfo { 137 | if (_value.modinfo == null) { 138 | return null; 139 | } 140 | 141 | return $ModinfoCopyWith<$Res>(_value.modinfo!, (value) { 142 | return _then(_value.copyWith(modinfo: value)); 143 | }); 144 | } 145 | } 146 | 147 | /// @nodoc 148 | abstract class _$$_ServerPingInfoCopyWith<$Res> 149 | implements $ServerPingInfoCopyWith<$Res> { 150 | factory _$$_ServerPingInfoCopyWith( 151 | _$_ServerPingInfo value, $Res Function(_$_ServerPingInfo) then) = 152 | __$$_ServerPingInfoCopyWithImpl<$Res>; 153 | @override 154 | $Res call( 155 | {Version version, 156 | Players players, 157 | @JsonKey(fromJson: _descriptionToJson) Description description, 158 | String? favicon, 159 | Modinfo? modinfo, 160 | int? ping}); 161 | 162 | @override 163 | $VersionCopyWith<$Res> get version; 164 | @override 165 | $PlayersCopyWith<$Res> get players; 166 | @override 167 | $DescriptionCopyWith<$Res> get description; 168 | @override 169 | $ModinfoCopyWith<$Res>? get modinfo; 170 | } 171 | 172 | /// @nodoc 173 | class __$$_ServerPingInfoCopyWithImpl<$Res> 174 | extends _$ServerPingInfoCopyWithImpl<$Res> 175 | implements _$$_ServerPingInfoCopyWith<$Res> { 176 | __$$_ServerPingInfoCopyWithImpl( 177 | _$_ServerPingInfo _value, $Res Function(_$_ServerPingInfo) _then) 178 | : super(_value, (v) => _then(v as _$_ServerPingInfo)); 179 | 180 | @override 181 | _$_ServerPingInfo get _value => super._value as _$_ServerPingInfo; 182 | 183 | @override 184 | $Res call({ 185 | Object? version = freezed, 186 | Object? players = freezed, 187 | Object? description = freezed, 188 | Object? favicon = freezed, 189 | Object? modinfo = freezed, 190 | Object? ping = freezed, 191 | }) { 192 | return _then(_$_ServerPingInfo( 193 | version: version == freezed 194 | ? _value.version 195 | : version // ignore: cast_nullable_to_non_nullable 196 | as Version, 197 | players: players == freezed 198 | ? _value.players 199 | : players // ignore: cast_nullable_to_non_nullable 200 | as Players, 201 | description: description == freezed 202 | ? _value.description 203 | : description // ignore: cast_nullable_to_non_nullable 204 | as Description, 205 | favicon: favicon == freezed 206 | ? _value.favicon 207 | : favicon // ignore: cast_nullable_to_non_nullable 208 | as String?, 209 | modinfo: modinfo == freezed 210 | ? _value.modinfo 211 | : modinfo // ignore: cast_nullable_to_non_nullable 212 | as Modinfo?, 213 | ping: ping == freezed 214 | ? _value.ping 215 | : ping // ignore: cast_nullable_to_non_nullable 216 | as int?, 217 | )); 218 | } 219 | } 220 | 221 | /// @nodoc 222 | @JsonSerializable() 223 | class _$_ServerPingInfo implements _ServerPingInfo { 224 | const _$_ServerPingInfo( 225 | {required this.version, 226 | required this.players, 227 | @JsonKey(fromJson: _descriptionToJson) required this.description, 228 | this.favicon, 229 | this.modinfo, 230 | this.ping}); 231 | 232 | factory _$_ServerPingInfo.fromJson(Map json) => 233 | _$$_ServerPingInfoFromJson(json); 234 | 235 | /// Server and protocol version. 236 | @override 237 | final Version version; 238 | 239 | /// Total and connected players. 240 | @override 241 | final Players players; 242 | 243 | /// MOTD description. 244 | // ignore: invalid_annotation_target 245 | @override 246 | @JsonKey(fromJson: _descriptionToJson) 247 | final Description description; 248 | 249 | /// Favicon data uri. 250 | @override 251 | final String? favicon; 252 | 253 | /// Server's mods. 254 | @override 255 | final Modinfo? modinfo; 256 | 257 | /// Server's ping. 258 | @override 259 | final int? ping; 260 | 261 | @override 262 | String toString() { 263 | return 'ServerPingInfo(version: $version, players: $players, description: $description, favicon: $favicon, modinfo: $modinfo, ping: $ping)'; 264 | } 265 | 266 | @override 267 | bool operator ==(dynamic other) { 268 | return identical(this, other) || 269 | (other.runtimeType == runtimeType && 270 | other is _$_ServerPingInfo && 271 | const DeepCollectionEquality().equals(other.version, version) && 272 | const DeepCollectionEquality().equals(other.players, players) && 273 | const DeepCollectionEquality() 274 | .equals(other.description, description) && 275 | const DeepCollectionEquality().equals(other.favicon, favicon) && 276 | const DeepCollectionEquality().equals(other.modinfo, modinfo) && 277 | const DeepCollectionEquality().equals(other.ping, ping)); 278 | } 279 | 280 | @JsonKey(ignore: true) 281 | @override 282 | int get hashCode => Object.hash( 283 | runtimeType, 284 | const DeepCollectionEquality().hash(version), 285 | const DeepCollectionEquality().hash(players), 286 | const DeepCollectionEquality().hash(description), 287 | const DeepCollectionEquality().hash(favicon), 288 | const DeepCollectionEquality().hash(modinfo), 289 | const DeepCollectionEquality().hash(ping)); 290 | 291 | @JsonKey(ignore: true) 292 | @override 293 | _$$_ServerPingInfoCopyWith<_$_ServerPingInfo> get copyWith => 294 | __$$_ServerPingInfoCopyWithImpl<_$_ServerPingInfo>(this, _$identity); 295 | 296 | @override 297 | Map toJson() { 298 | return _$$_ServerPingInfoToJson(this); 299 | } 300 | } 301 | 302 | abstract class _ServerPingInfo implements ServerPingInfo { 303 | const factory _ServerPingInfo( 304 | {required final Version version, 305 | required final Players players, 306 | @JsonKey(fromJson: _descriptionToJson) 307 | required final Description description, 308 | final String? favicon, 309 | final Modinfo? modinfo, 310 | final int? ping}) = _$_ServerPingInfo; 311 | 312 | factory _ServerPingInfo.fromJson(Map json) = 313 | _$_ServerPingInfo.fromJson; 314 | 315 | @override 316 | 317 | /// Server and protocol version. 318 | Version get version => throw _privateConstructorUsedError; 319 | @override 320 | 321 | /// Total and connected players. 322 | Players get players => throw _privateConstructorUsedError; 323 | @override 324 | 325 | /// MOTD description. 326 | // ignore: invalid_annotation_target 327 | @JsonKey(fromJson: _descriptionToJson) 328 | Description get description => throw _privateConstructorUsedError; 329 | @override 330 | 331 | /// Favicon data uri. 332 | String? get favicon => throw _privateConstructorUsedError; 333 | @override 334 | 335 | /// Server's mods. 336 | Modinfo? get modinfo => throw _privateConstructorUsedError; 337 | @override 338 | 339 | /// Server's ping. 340 | int? get ping => throw _privateConstructorUsedError; 341 | @override 342 | @JsonKey(ignore: true) 343 | _$$_ServerPingInfoCopyWith<_$_ServerPingInfo> get copyWith => 344 | throw _privateConstructorUsedError; 345 | } 346 | 347 | Description _$DescriptionFromJson(Map json) { 348 | return _Description.fromJson(json); 349 | } 350 | 351 | /// @nodoc 352 | mixin _$Description { 353 | /// Extra MOTD text. 354 | List? get extra => throw _privateConstructorUsedError; 355 | 356 | /// Motd text. 357 | String get text => throw _privateConstructorUsedError; 358 | 359 | Map toJson() => throw _privateConstructorUsedError; 360 | @JsonKey(ignore: true) 361 | $DescriptionCopyWith get copyWith => 362 | throw _privateConstructorUsedError; 363 | } 364 | 365 | /// @nodoc 366 | abstract class $DescriptionCopyWith<$Res> { 367 | factory $DescriptionCopyWith( 368 | Description value, $Res Function(Description) then) = 369 | _$DescriptionCopyWithImpl<$Res>; 370 | $Res call({List? extra, String text}); 371 | } 372 | 373 | /// @nodoc 374 | class _$DescriptionCopyWithImpl<$Res> implements $DescriptionCopyWith<$Res> { 375 | _$DescriptionCopyWithImpl(this._value, this._then); 376 | 377 | final Description _value; 378 | // ignore: unused_field 379 | final $Res Function(Description) _then; 380 | 381 | @override 382 | $Res call({ 383 | Object? extra = freezed, 384 | Object? text = freezed, 385 | }) { 386 | return _then(_value.copyWith( 387 | extra: extra == freezed 388 | ? _value.extra 389 | : extra // ignore: cast_nullable_to_non_nullable 390 | as List?, 391 | text: text == freezed 392 | ? _value.text 393 | : text // ignore: cast_nullable_to_non_nullable 394 | as String, 395 | )); 396 | } 397 | } 398 | 399 | /// @nodoc 400 | abstract class _$$_DescriptionCopyWith<$Res> 401 | implements $DescriptionCopyWith<$Res> { 402 | factory _$$_DescriptionCopyWith( 403 | _$_Description value, $Res Function(_$_Description) then) = 404 | __$$_DescriptionCopyWithImpl<$Res>; 405 | @override 406 | $Res call({List? extra, String text}); 407 | } 408 | 409 | /// @nodoc 410 | class __$$_DescriptionCopyWithImpl<$Res> extends _$DescriptionCopyWithImpl<$Res> 411 | implements _$$_DescriptionCopyWith<$Res> { 412 | __$$_DescriptionCopyWithImpl( 413 | _$_Description _value, $Res Function(_$_Description) _then) 414 | : super(_value, (v) => _then(v as _$_Description)); 415 | 416 | @override 417 | _$_Description get _value => super._value as _$_Description; 418 | 419 | @override 420 | $Res call({ 421 | Object? extra = freezed, 422 | Object? text = freezed, 423 | }) { 424 | return _then(_$_Description( 425 | extra: extra == freezed 426 | ? _value._extra 427 | : extra // ignore: cast_nullable_to_non_nullable 428 | as List?, 429 | text: text == freezed 430 | ? _value.text 431 | : text // ignore: cast_nullable_to_non_nullable 432 | as String, 433 | )); 434 | } 435 | } 436 | 437 | /// @nodoc 438 | @JsonSerializable() 439 | class _$_Description implements _Description { 440 | const _$_Description({final List? extra, required this.text}) 441 | : _extra = extra; 442 | 443 | factory _$_Description.fromJson(Map json) => 444 | _$$_DescriptionFromJson(json); 445 | 446 | /// Extra MOTD text. 447 | final List? _extra; 448 | 449 | /// Extra MOTD text. 450 | @override 451 | List? get extra { 452 | final value = _extra; 453 | if (value == null) return null; 454 | // ignore: implicit_dynamic_type 455 | return EqualUnmodifiableListView(value); 456 | } 457 | 458 | /// Motd text. 459 | @override 460 | final String text; 461 | 462 | @override 463 | String toString() { 464 | return 'Description(extra: $extra, text: $text)'; 465 | } 466 | 467 | @override 468 | bool operator ==(dynamic other) { 469 | return identical(this, other) || 470 | (other.runtimeType == runtimeType && 471 | other is _$_Description && 472 | const DeepCollectionEquality().equals(other._extra, _extra) && 473 | const DeepCollectionEquality().equals(other.text, text)); 474 | } 475 | 476 | @JsonKey(ignore: true) 477 | @override 478 | int get hashCode => Object.hash( 479 | runtimeType, 480 | const DeepCollectionEquality().hash(_extra), 481 | const DeepCollectionEquality().hash(text)); 482 | 483 | @JsonKey(ignore: true) 484 | @override 485 | _$$_DescriptionCopyWith<_$_Description> get copyWith => 486 | __$$_DescriptionCopyWithImpl<_$_Description>(this, _$identity); 487 | 488 | @override 489 | Map toJson() { 490 | return _$$_DescriptionToJson(this); 491 | } 492 | } 493 | 494 | abstract class _Description implements Description { 495 | const factory _Description( 496 | {final List? extra, required final String text}) = _$_Description; 497 | 498 | factory _Description.fromJson(Map json) = 499 | _$_Description.fromJson; 500 | 501 | @override 502 | 503 | /// Extra MOTD text. 504 | List? get extra => throw _privateConstructorUsedError; 505 | @override 506 | 507 | /// Motd text. 508 | String get text => throw _privateConstructorUsedError; 509 | @override 510 | @JsonKey(ignore: true) 511 | _$$_DescriptionCopyWith<_$_Description> get copyWith => 512 | throw _privateConstructorUsedError; 513 | } 514 | 515 | Extra _$ExtraFromJson(Map json) { 516 | return _Extra.fromJson(json); 517 | } 518 | 519 | /// @nodoc 520 | mixin _$Extra { 521 | /// Text color. 522 | String? get color => throw _privateConstructorUsedError; 523 | 524 | /// Actual text. 525 | String get text => throw _privateConstructorUsedError; 526 | 527 | /// Bold text. 528 | bool? get bold => throw _privateConstructorUsedError; 529 | 530 | /// Strikethrough text. 531 | bool? get strikethrough => throw _privateConstructorUsedError; 532 | 533 | /// Italic text. 534 | bool? get italic => throw _privateConstructorUsedError; 535 | 536 | /// Underlined text. 537 | bool? get underlined => throw _privateConstructorUsedError; 538 | 539 | Map toJson() => throw _privateConstructorUsedError; 540 | @JsonKey(ignore: true) 541 | $ExtraCopyWith get copyWith => throw _privateConstructorUsedError; 542 | } 543 | 544 | /// @nodoc 545 | abstract class $ExtraCopyWith<$Res> { 546 | factory $ExtraCopyWith(Extra value, $Res Function(Extra) then) = 547 | _$ExtraCopyWithImpl<$Res>; 548 | $Res call( 549 | {String? color, 550 | String text, 551 | bool? bold, 552 | bool? strikethrough, 553 | bool? italic, 554 | bool? underlined}); 555 | } 556 | 557 | /// @nodoc 558 | class _$ExtraCopyWithImpl<$Res> implements $ExtraCopyWith<$Res> { 559 | _$ExtraCopyWithImpl(this._value, this._then); 560 | 561 | final Extra _value; 562 | // ignore: unused_field 563 | final $Res Function(Extra) _then; 564 | 565 | @override 566 | $Res call({ 567 | Object? color = freezed, 568 | Object? text = freezed, 569 | Object? bold = freezed, 570 | Object? strikethrough = freezed, 571 | Object? italic = freezed, 572 | Object? underlined = freezed, 573 | }) { 574 | return _then(_value.copyWith( 575 | color: color == freezed 576 | ? _value.color 577 | : color // ignore: cast_nullable_to_non_nullable 578 | as String?, 579 | text: text == freezed 580 | ? _value.text 581 | : text // ignore: cast_nullable_to_non_nullable 582 | as String, 583 | bold: bold == freezed 584 | ? _value.bold 585 | : bold // ignore: cast_nullable_to_non_nullable 586 | as bool?, 587 | strikethrough: strikethrough == freezed 588 | ? _value.strikethrough 589 | : strikethrough // ignore: cast_nullable_to_non_nullable 590 | as bool?, 591 | italic: italic == freezed 592 | ? _value.italic 593 | : italic // ignore: cast_nullable_to_non_nullable 594 | as bool?, 595 | underlined: underlined == freezed 596 | ? _value.underlined 597 | : underlined // ignore: cast_nullable_to_non_nullable 598 | as bool?, 599 | )); 600 | } 601 | } 602 | 603 | /// @nodoc 604 | abstract class _$$_ExtraCopyWith<$Res> implements $ExtraCopyWith<$Res> { 605 | factory _$$_ExtraCopyWith(_$_Extra value, $Res Function(_$_Extra) then) = 606 | __$$_ExtraCopyWithImpl<$Res>; 607 | @override 608 | $Res call( 609 | {String? color, 610 | String text, 611 | bool? bold, 612 | bool? strikethrough, 613 | bool? italic, 614 | bool? underlined}); 615 | } 616 | 617 | /// @nodoc 618 | class __$$_ExtraCopyWithImpl<$Res> extends _$ExtraCopyWithImpl<$Res> 619 | implements _$$_ExtraCopyWith<$Res> { 620 | __$$_ExtraCopyWithImpl(_$_Extra _value, $Res Function(_$_Extra) _then) 621 | : super(_value, (v) => _then(v as _$_Extra)); 622 | 623 | @override 624 | _$_Extra get _value => super._value as _$_Extra; 625 | 626 | @override 627 | $Res call({ 628 | Object? color = freezed, 629 | Object? text = freezed, 630 | Object? bold = freezed, 631 | Object? strikethrough = freezed, 632 | Object? italic = freezed, 633 | Object? underlined = freezed, 634 | }) { 635 | return _then(_$_Extra( 636 | color: color == freezed 637 | ? _value.color 638 | : color // ignore: cast_nullable_to_non_nullable 639 | as String?, 640 | text: text == freezed 641 | ? _value.text 642 | : text // ignore: cast_nullable_to_non_nullable 643 | as String, 644 | bold: bold == freezed 645 | ? _value.bold 646 | : bold // ignore: cast_nullable_to_non_nullable 647 | as bool?, 648 | strikethrough: strikethrough == freezed 649 | ? _value.strikethrough 650 | : strikethrough // ignore: cast_nullable_to_non_nullable 651 | as bool?, 652 | italic: italic == freezed 653 | ? _value.italic 654 | : italic // ignore: cast_nullable_to_non_nullable 655 | as bool?, 656 | underlined: underlined == freezed 657 | ? _value.underlined 658 | : underlined // ignore: cast_nullable_to_non_nullable 659 | as bool?, 660 | )); 661 | } 662 | } 663 | 664 | /// @nodoc 665 | @JsonSerializable() 666 | class _$_Extra implements _Extra { 667 | const _$_Extra( 668 | {this.color, 669 | required this.text, 670 | this.bold, 671 | this.strikethrough, 672 | this.italic, 673 | this.underlined}); 674 | 675 | factory _$_Extra.fromJson(Map json) => 676 | _$$_ExtraFromJson(json); 677 | 678 | /// Text color. 679 | @override 680 | final String? color; 681 | 682 | /// Actual text. 683 | @override 684 | final String text; 685 | 686 | /// Bold text. 687 | @override 688 | final bool? bold; 689 | 690 | /// Strikethrough text. 691 | @override 692 | final bool? strikethrough; 693 | 694 | /// Italic text. 695 | @override 696 | final bool? italic; 697 | 698 | /// Underlined text. 699 | @override 700 | final bool? underlined; 701 | 702 | @override 703 | String toString() { 704 | return 'Extra(color: $color, text: $text, bold: $bold, strikethrough: $strikethrough, italic: $italic, underlined: $underlined)'; 705 | } 706 | 707 | @override 708 | bool operator ==(dynamic other) { 709 | return identical(this, other) || 710 | (other.runtimeType == runtimeType && 711 | other is _$_Extra && 712 | const DeepCollectionEquality().equals(other.color, color) && 713 | const DeepCollectionEquality().equals(other.text, text) && 714 | const DeepCollectionEquality().equals(other.bold, bold) && 715 | const DeepCollectionEquality() 716 | .equals(other.strikethrough, strikethrough) && 717 | const DeepCollectionEquality().equals(other.italic, italic) && 718 | const DeepCollectionEquality() 719 | .equals(other.underlined, underlined)); 720 | } 721 | 722 | @JsonKey(ignore: true) 723 | @override 724 | int get hashCode => Object.hash( 725 | runtimeType, 726 | const DeepCollectionEquality().hash(color), 727 | const DeepCollectionEquality().hash(text), 728 | const DeepCollectionEquality().hash(bold), 729 | const DeepCollectionEquality().hash(strikethrough), 730 | const DeepCollectionEquality().hash(italic), 731 | const DeepCollectionEquality().hash(underlined)); 732 | 733 | @JsonKey(ignore: true) 734 | @override 735 | _$$_ExtraCopyWith<_$_Extra> get copyWith => 736 | __$$_ExtraCopyWithImpl<_$_Extra>(this, _$identity); 737 | 738 | @override 739 | Map toJson() { 740 | return _$$_ExtraToJson(this); 741 | } 742 | } 743 | 744 | abstract class _Extra implements Extra { 745 | const factory _Extra( 746 | {final String? color, 747 | required final String text, 748 | final bool? bold, 749 | final bool? strikethrough, 750 | final bool? italic, 751 | final bool? underlined}) = _$_Extra; 752 | 753 | factory _Extra.fromJson(Map json) = _$_Extra.fromJson; 754 | 755 | @override 756 | 757 | /// Text color. 758 | String? get color => throw _privateConstructorUsedError; 759 | @override 760 | 761 | /// Actual text. 762 | String get text => throw _privateConstructorUsedError; 763 | @override 764 | 765 | /// Bold text. 766 | bool? get bold => throw _privateConstructorUsedError; 767 | @override 768 | 769 | /// Strikethrough text. 770 | bool? get strikethrough => throw _privateConstructorUsedError; 771 | @override 772 | 773 | /// Italic text. 774 | bool? get italic => throw _privateConstructorUsedError; 775 | @override 776 | 777 | /// Underlined text. 778 | bool? get underlined => throw _privateConstructorUsedError; 779 | @override 780 | @JsonKey(ignore: true) 781 | _$$_ExtraCopyWith<_$_Extra> get copyWith => 782 | throw _privateConstructorUsedError; 783 | } 784 | 785 | Modinfo _$ModinfoFromJson(Map json) { 786 | return _Modinfo.fromJson(json); 787 | } 788 | 789 | /// @nodoc 790 | mixin _$Modinfo { 791 | /// Mods type. 792 | String get type => throw _privateConstructorUsedError; 793 | 794 | /// Mods list. 795 | List get modList => throw _privateConstructorUsedError; 796 | 797 | Map toJson() => throw _privateConstructorUsedError; 798 | @JsonKey(ignore: true) 799 | $ModinfoCopyWith get copyWith => throw _privateConstructorUsedError; 800 | } 801 | 802 | /// @nodoc 803 | abstract class $ModinfoCopyWith<$Res> { 804 | factory $ModinfoCopyWith(Modinfo value, $Res Function(Modinfo) then) = 805 | _$ModinfoCopyWithImpl<$Res>; 806 | $Res call({String type, List modList}); 807 | } 808 | 809 | /// @nodoc 810 | class _$ModinfoCopyWithImpl<$Res> implements $ModinfoCopyWith<$Res> { 811 | _$ModinfoCopyWithImpl(this._value, this._then); 812 | 813 | final Modinfo _value; 814 | // ignore: unused_field 815 | final $Res Function(Modinfo) _then; 816 | 817 | @override 818 | $Res call({ 819 | Object? type = freezed, 820 | Object? modList = freezed, 821 | }) { 822 | return _then(_value.copyWith( 823 | type: type == freezed 824 | ? _value.type 825 | : type // ignore: cast_nullable_to_non_nullable 826 | as String, 827 | modList: modList == freezed 828 | ? _value.modList 829 | : modList // ignore: cast_nullable_to_non_nullable 830 | as List, 831 | )); 832 | } 833 | } 834 | 835 | /// @nodoc 836 | abstract class _$$_ModinfoCopyWith<$Res> implements $ModinfoCopyWith<$Res> { 837 | factory _$$_ModinfoCopyWith( 838 | _$_Modinfo value, $Res Function(_$_Modinfo) then) = 839 | __$$_ModinfoCopyWithImpl<$Res>; 840 | @override 841 | $Res call({String type, List modList}); 842 | } 843 | 844 | /// @nodoc 845 | class __$$_ModinfoCopyWithImpl<$Res> extends _$ModinfoCopyWithImpl<$Res> 846 | implements _$$_ModinfoCopyWith<$Res> { 847 | __$$_ModinfoCopyWithImpl(_$_Modinfo _value, $Res Function(_$_Modinfo) _then) 848 | : super(_value, (v) => _then(v as _$_Modinfo)); 849 | 850 | @override 851 | _$_Modinfo get _value => super._value as _$_Modinfo; 852 | 853 | @override 854 | $Res call({ 855 | Object? type = freezed, 856 | Object? modList = freezed, 857 | }) { 858 | return _then(_$_Modinfo( 859 | type: type == freezed 860 | ? _value.type 861 | : type // ignore: cast_nullable_to_non_nullable 862 | as String, 863 | modList: modList == freezed 864 | ? _value._modList 865 | : modList // ignore: cast_nullable_to_non_nullable 866 | as List, 867 | )); 868 | } 869 | } 870 | 871 | /// @nodoc 872 | @JsonSerializable() 873 | class _$_Modinfo implements _Modinfo { 874 | const _$_Modinfo({required this.type, required final List modList}) 875 | : _modList = modList; 876 | 877 | factory _$_Modinfo.fromJson(Map json) => 878 | _$$_ModinfoFromJson(json); 879 | 880 | /// Mods type. 881 | @override 882 | final String type; 883 | 884 | /// Mods list. 885 | final List _modList; 886 | 887 | /// Mods list. 888 | @override 889 | List get modList { 890 | // ignore: implicit_dynamic_type 891 | return EqualUnmodifiableListView(_modList); 892 | } 893 | 894 | @override 895 | String toString() { 896 | return 'Modinfo(type: $type, modList: $modList)'; 897 | } 898 | 899 | @override 900 | bool operator ==(dynamic other) { 901 | return identical(this, other) || 902 | (other.runtimeType == runtimeType && 903 | other is _$_Modinfo && 904 | const DeepCollectionEquality().equals(other.type, type) && 905 | const DeepCollectionEquality().equals(other._modList, _modList)); 906 | } 907 | 908 | @JsonKey(ignore: true) 909 | @override 910 | int get hashCode => Object.hash( 911 | runtimeType, 912 | const DeepCollectionEquality().hash(type), 913 | const DeepCollectionEquality().hash(_modList)); 914 | 915 | @JsonKey(ignore: true) 916 | @override 917 | _$$_ModinfoCopyWith<_$_Modinfo> get copyWith => 918 | __$$_ModinfoCopyWithImpl<_$_Modinfo>(this, _$identity); 919 | 920 | @override 921 | Map toJson() { 922 | return _$$_ModinfoToJson(this); 923 | } 924 | } 925 | 926 | abstract class _Modinfo implements Modinfo { 927 | const factory _Modinfo( 928 | {required final String type, 929 | required final List modList}) = _$_Modinfo; 930 | 931 | factory _Modinfo.fromJson(Map json) = _$_Modinfo.fromJson; 932 | 933 | @override 934 | 935 | /// Mods type. 936 | String get type => throw _privateConstructorUsedError; 937 | @override 938 | 939 | /// Mods list. 940 | List get modList => throw _privateConstructorUsedError; 941 | @override 942 | @JsonKey(ignore: true) 943 | _$$_ModinfoCopyWith<_$_Modinfo> get copyWith => 944 | throw _privateConstructorUsedError; 945 | } 946 | 947 | Players _$PlayersFromJson(Map json) { 948 | return _Players.fromJson(json); 949 | } 950 | 951 | /// @nodoc 952 | mixin _$Players { 953 | /// Max players. 954 | int get max => throw _privateConstructorUsedError; 955 | 956 | /// Connected players. 957 | int get online => throw _privateConstructorUsedError; 958 | 959 | /// Connected players list. 960 | List? get sample => throw _privateConstructorUsedError; 961 | 962 | Map toJson() => throw _privateConstructorUsedError; 963 | @JsonKey(ignore: true) 964 | $PlayersCopyWith get copyWith => throw _privateConstructorUsedError; 965 | } 966 | 967 | /// @nodoc 968 | abstract class $PlayersCopyWith<$Res> { 969 | factory $PlayersCopyWith(Players value, $Res Function(Players) then) = 970 | _$PlayersCopyWithImpl<$Res>; 971 | $Res call({int max, int online, List? sample}); 972 | } 973 | 974 | /// @nodoc 975 | class _$PlayersCopyWithImpl<$Res> implements $PlayersCopyWith<$Res> { 976 | _$PlayersCopyWithImpl(this._value, this._then); 977 | 978 | final Players _value; 979 | // ignore: unused_field 980 | final $Res Function(Players) _then; 981 | 982 | @override 983 | $Res call({ 984 | Object? max = freezed, 985 | Object? online = freezed, 986 | Object? sample = freezed, 987 | }) { 988 | return _then(_value.copyWith( 989 | max: max == freezed 990 | ? _value.max 991 | : max // ignore: cast_nullable_to_non_nullable 992 | as int, 993 | online: online == freezed 994 | ? _value.online 995 | : online // ignore: cast_nullable_to_non_nullable 996 | as int, 997 | sample: sample == freezed 998 | ? _value.sample 999 | : sample // ignore: cast_nullable_to_non_nullable 1000 | as List?, 1001 | )); 1002 | } 1003 | } 1004 | 1005 | /// @nodoc 1006 | abstract class _$$_PlayersCopyWith<$Res> implements $PlayersCopyWith<$Res> { 1007 | factory _$$_PlayersCopyWith( 1008 | _$_Players value, $Res Function(_$_Players) then) = 1009 | __$$_PlayersCopyWithImpl<$Res>; 1010 | @override 1011 | $Res call({int max, int online, List? sample}); 1012 | } 1013 | 1014 | /// @nodoc 1015 | class __$$_PlayersCopyWithImpl<$Res> extends _$PlayersCopyWithImpl<$Res> 1016 | implements _$$_PlayersCopyWith<$Res> { 1017 | __$$_PlayersCopyWithImpl(_$_Players _value, $Res Function(_$_Players) _then) 1018 | : super(_value, (v) => _then(v as _$_Players)); 1019 | 1020 | @override 1021 | _$_Players get _value => super._value as _$_Players; 1022 | 1023 | @override 1024 | $Res call({ 1025 | Object? max = freezed, 1026 | Object? online = freezed, 1027 | Object? sample = freezed, 1028 | }) { 1029 | return _then(_$_Players( 1030 | max: max == freezed 1031 | ? _value.max 1032 | : max // ignore: cast_nullable_to_non_nullable 1033 | as int, 1034 | online: online == freezed 1035 | ? _value.online 1036 | : online // ignore: cast_nullable_to_non_nullable 1037 | as int, 1038 | sample: sample == freezed 1039 | ? _value._sample 1040 | : sample // ignore: cast_nullable_to_non_nullable 1041 | as List?, 1042 | )); 1043 | } 1044 | } 1045 | 1046 | /// @nodoc 1047 | @JsonSerializable() 1048 | class _$_Players implements _Players { 1049 | const _$_Players( 1050 | {required this.max, required this.online, final List? sample}) 1051 | : _sample = sample; 1052 | 1053 | factory _$_Players.fromJson(Map json) => 1054 | _$$_PlayersFromJson(json); 1055 | 1056 | /// Max players. 1057 | @override 1058 | final int max; 1059 | 1060 | /// Connected players. 1061 | @override 1062 | final int online; 1063 | 1064 | /// Connected players list. 1065 | final List? _sample; 1066 | 1067 | /// Connected players list. 1068 | @override 1069 | List? get sample { 1070 | final value = _sample; 1071 | if (value == null) return null; 1072 | // ignore: implicit_dynamic_type 1073 | return EqualUnmodifiableListView(value); 1074 | } 1075 | 1076 | @override 1077 | String toString() { 1078 | return 'Players(max: $max, online: $online, sample: $sample)'; 1079 | } 1080 | 1081 | @override 1082 | bool operator ==(dynamic other) { 1083 | return identical(this, other) || 1084 | (other.runtimeType == runtimeType && 1085 | other is _$_Players && 1086 | const DeepCollectionEquality().equals(other.max, max) && 1087 | const DeepCollectionEquality().equals(other.online, online) && 1088 | const DeepCollectionEquality().equals(other._sample, _sample)); 1089 | } 1090 | 1091 | @JsonKey(ignore: true) 1092 | @override 1093 | int get hashCode => Object.hash( 1094 | runtimeType, 1095 | const DeepCollectionEquality().hash(max), 1096 | const DeepCollectionEquality().hash(online), 1097 | const DeepCollectionEquality().hash(_sample)); 1098 | 1099 | @JsonKey(ignore: true) 1100 | @override 1101 | _$$_PlayersCopyWith<_$_Players> get copyWith => 1102 | __$$_PlayersCopyWithImpl<_$_Players>(this, _$identity); 1103 | 1104 | @override 1105 | Map toJson() { 1106 | return _$$_PlayersToJson(this); 1107 | } 1108 | } 1109 | 1110 | abstract class _Players implements Players { 1111 | const factory _Players( 1112 | {required final int max, 1113 | required final int online, 1114 | final List? sample}) = _$_Players; 1115 | 1116 | factory _Players.fromJson(Map json) = _$_Players.fromJson; 1117 | 1118 | @override 1119 | 1120 | /// Max players. 1121 | int get max => throw _privateConstructorUsedError; 1122 | @override 1123 | 1124 | /// Connected players. 1125 | int get online => throw _privateConstructorUsedError; 1126 | @override 1127 | 1128 | /// Connected players list. 1129 | List? get sample => throw _privateConstructorUsedError; 1130 | @override 1131 | @JsonKey(ignore: true) 1132 | _$$_PlayersCopyWith<_$_Players> get copyWith => 1133 | throw _privateConstructorUsedError; 1134 | } 1135 | 1136 | Sample _$SampleFromJson(Map json) { 1137 | return _Sample.fromJson(json); 1138 | } 1139 | 1140 | /// @nodoc 1141 | mixin _$Sample { 1142 | /// Players' UUID. 1143 | String get id => throw _privateConstructorUsedError; 1144 | 1145 | /// Players' name. 1146 | String get name => throw _privateConstructorUsedError; 1147 | 1148 | Map toJson() => throw _privateConstructorUsedError; 1149 | @JsonKey(ignore: true) 1150 | $SampleCopyWith get copyWith => throw _privateConstructorUsedError; 1151 | } 1152 | 1153 | /// @nodoc 1154 | abstract class $SampleCopyWith<$Res> { 1155 | factory $SampleCopyWith(Sample value, $Res Function(Sample) then) = 1156 | _$SampleCopyWithImpl<$Res>; 1157 | $Res call({String id, String name}); 1158 | } 1159 | 1160 | /// @nodoc 1161 | class _$SampleCopyWithImpl<$Res> implements $SampleCopyWith<$Res> { 1162 | _$SampleCopyWithImpl(this._value, this._then); 1163 | 1164 | final Sample _value; 1165 | // ignore: unused_field 1166 | final $Res Function(Sample) _then; 1167 | 1168 | @override 1169 | $Res call({ 1170 | Object? id = freezed, 1171 | Object? name = freezed, 1172 | }) { 1173 | return _then(_value.copyWith( 1174 | id: id == freezed 1175 | ? _value.id 1176 | : id // ignore: cast_nullable_to_non_nullable 1177 | as String, 1178 | name: name == freezed 1179 | ? _value.name 1180 | : name // ignore: cast_nullable_to_non_nullable 1181 | as String, 1182 | )); 1183 | } 1184 | } 1185 | 1186 | /// @nodoc 1187 | abstract class _$$_SampleCopyWith<$Res> implements $SampleCopyWith<$Res> { 1188 | factory _$$_SampleCopyWith(_$_Sample value, $Res Function(_$_Sample) then) = 1189 | __$$_SampleCopyWithImpl<$Res>; 1190 | @override 1191 | $Res call({String id, String name}); 1192 | } 1193 | 1194 | /// @nodoc 1195 | class __$$_SampleCopyWithImpl<$Res> extends _$SampleCopyWithImpl<$Res> 1196 | implements _$$_SampleCopyWith<$Res> { 1197 | __$$_SampleCopyWithImpl(_$_Sample _value, $Res Function(_$_Sample) _then) 1198 | : super(_value, (v) => _then(v as _$_Sample)); 1199 | 1200 | @override 1201 | _$_Sample get _value => super._value as _$_Sample; 1202 | 1203 | @override 1204 | $Res call({ 1205 | Object? id = freezed, 1206 | Object? name = freezed, 1207 | }) { 1208 | return _then(_$_Sample( 1209 | id: id == freezed 1210 | ? _value.id 1211 | : id // ignore: cast_nullable_to_non_nullable 1212 | as String, 1213 | name: name == freezed 1214 | ? _value.name 1215 | : name // ignore: cast_nullable_to_non_nullable 1216 | as String, 1217 | )); 1218 | } 1219 | } 1220 | 1221 | /// @nodoc 1222 | @JsonSerializable() 1223 | class _$_Sample implements _Sample { 1224 | const _$_Sample({required this.id, required this.name}); 1225 | 1226 | factory _$_Sample.fromJson(Map json) => 1227 | _$$_SampleFromJson(json); 1228 | 1229 | /// Players' UUID. 1230 | @override 1231 | final String id; 1232 | 1233 | /// Players' name. 1234 | @override 1235 | final String name; 1236 | 1237 | @override 1238 | String toString() { 1239 | return 'Sample(id: $id, name: $name)'; 1240 | } 1241 | 1242 | @override 1243 | bool operator ==(dynamic other) { 1244 | return identical(this, other) || 1245 | (other.runtimeType == runtimeType && 1246 | other is _$_Sample && 1247 | const DeepCollectionEquality().equals(other.id, id) && 1248 | const DeepCollectionEquality().equals(other.name, name)); 1249 | } 1250 | 1251 | @JsonKey(ignore: true) 1252 | @override 1253 | int get hashCode => Object.hash( 1254 | runtimeType, 1255 | const DeepCollectionEquality().hash(id), 1256 | const DeepCollectionEquality().hash(name)); 1257 | 1258 | @JsonKey(ignore: true) 1259 | @override 1260 | _$$_SampleCopyWith<_$_Sample> get copyWith => 1261 | __$$_SampleCopyWithImpl<_$_Sample>(this, _$identity); 1262 | 1263 | @override 1264 | Map toJson() { 1265 | return _$$_SampleToJson(this); 1266 | } 1267 | } 1268 | 1269 | abstract class _Sample implements Sample { 1270 | const factory _Sample( 1271 | {required final String id, required final String name}) = _$_Sample; 1272 | 1273 | factory _Sample.fromJson(Map json) = _$_Sample.fromJson; 1274 | 1275 | @override 1276 | 1277 | /// Players' UUID. 1278 | String get id => throw _privateConstructorUsedError; 1279 | @override 1280 | 1281 | /// Players' name. 1282 | String get name => throw _privateConstructorUsedError; 1283 | @override 1284 | @JsonKey(ignore: true) 1285 | _$$_SampleCopyWith<_$_Sample> get copyWith => 1286 | throw _privateConstructorUsedError; 1287 | } 1288 | 1289 | Version _$VersionFromJson(Map json) { 1290 | return _Version.fromJson(json); 1291 | } 1292 | 1293 | /// @nodoc 1294 | mixin _$Version { 1295 | /// Version name. 1296 | String get name => throw _privateConstructorUsedError; 1297 | 1298 | /// Version protocol. 1299 | int get protocol => throw _privateConstructorUsedError; 1300 | 1301 | Map toJson() => throw _privateConstructorUsedError; 1302 | @JsonKey(ignore: true) 1303 | $VersionCopyWith get copyWith => throw _privateConstructorUsedError; 1304 | } 1305 | 1306 | /// @nodoc 1307 | abstract class $VersionCopyWith<$Res> { 1308 | factory $VersionCopyWith(Version value, $Res Function(Version) then) = 1309 | _$VersionCopyWithImpl<$Res>; 1310 | $Res call({String name, int protocol}); 1311 | } 1312 | 1313 | /// @nodoc 1314 | class _$VersionCopyWithImpl<$Res> implements $VersionCopyWith<$Res> { 1315 | _$VersionCopyWithImpl(this._value, this._then); 1316 | 1317 | final Version _value; 1318 | // ignore: unused_field 1319 | final $Res Function(Version) _then; 1320 | 1321 | @override 1322 | $Res call({ 1323 | Object? name = freezed, 1324 | Object? protocol = freezed, 1325 | }) { 1326 | return _then(_value.copyWith( 1327 | name: name == freezed 1328 | ? _value.name 1329 | : name // ignore: cast_nullable_to_non_nullable 1330 | as String, 1331 | protocol: protocol == freezed 1332 | ? _value.protocol 1333 | : protocol // ignore: cast_nullable_to_non_nullable 1334 | as int, 1335 | )); 1336 | } 1337 | } 1338 | 1339 | /// @nodoc 1340 | abstract class _$$_VersionCopyWith<$Res> implements $VersionCopyWith<$Res> { 1341 | factory _$$_VersionCopyWith( 1342 | _$_Version value, $Res Function(_$_Version) then) = 1343 | __$$_VersionCopyWithImpl<$Res>; 1344 | @override 1345 | $Res call({String name, int protocol}); 1346 | } 1347 | 1348 | /// @nodoc 1349 | class __$$_VersionCopyWithImpl<$Res> extends _$VersionCopyWithImpl<$Res> 1350 | implements _$$_VersionCopyWith<$Res> { 1351 | __$$_VersionCopyWithImpl(_$_Version _value, $Res Function(_$_Version) _then) 1352 | : super(_value, (v) => _then(v as _$_Version)); 1353 | 1354 | @override 1355 | _$_Version get _value => super._value as _$_Version; 1356 | 1357 | @override 1358 | $Res call({ 1359 | Object? name = freezed, 1360 | Object? protocol = freezed, 1361 | }) { 1362 | return _then(_$_Version( 1363 | name: name == freezed 1364 | ? _value.name 1365 | : name // ignore: cast_nullable_to_non_nullable 1366 | as String, 1367 | protocol: protocol == freezed 1368 | ? _value.protocol 1369 | : protocol // ignore: cast_nullable_to_non_nullable 1370 | as int, 1371 | )); 1372 | } 1373 | } 1374 | 1375 | /// @nodoc 1376 | @JsonSerializable() 1377 | class _$_Version implements _Version { 1378 | const _$_Version({required this.name, required this.protocol}); 1379 | 1380 | factory _$_Version.fromJson(Map json) => 1381 | _$$_VersionFromJson(json); 1382 | 1383 | /// Version name. 1384 | @override 1385 | final String name; 1386 | 1387 | /// Version protocol. 1388 | @override 1389 | final int protocol; 1390 | 1391 | @override 1392 | String toString() { 1393 | return 'Version(name: $name, protocol: $protocol)'; 1394 | } 1395 | 1396 | @override 1397 | bool operator ==(dynamic other) { 1398 | return identical(this, other) || 1399 | (other.runtimeType == runtimeType && 1400 | other is _$_Version && 1401 | const DeepCollectionEquality().equals(other.name, name) && 1402 | const DeepCollectionEquality().equals(other.protocol, protocol)); 1403 | } 1404 | 1405 | @JsonKey(ignore: true) 1406 | @override 1407 | int get hashCode => Object.hash( 1408 | runtimeType, 1409 | const DeepCollectionEquality().hash(name), 1410 | const DeepCollectionEquality().hash(protocol)); 1411 | 1412 | @JsonKey(ignore: true) 1413 | @override 1414 | _$$_VersionCopyWith<_$_Version> get copyWith => 1415 | __$$_VersionCopyWithImpl<_$_Version>(this, _$identity); 1416 | 1417 | @override 1418 | Map toJson() { 1419 | return _$$_VersionToJson(this); 1420 | } 1421 | } 1422 | 1423 | abstract class _Version implements Version { 1424 | const factory _Version( 1425 | {required final String name, required final int protocol}) = _$_Version; 1426 | 1427 | factory _Version.fromJson(Map json) = _$_Version.fromJson; 1428 | 1429 | @override 1430 | 1431 | /// Version name. 1432 | String get name => throw _privateConstructorUsedError; 1433 | @override 1434 | 1435 | /// Version protocol. 1436 | int get protocol => throw _privateConstructorUsedError; 1437 | @override 1438 | @JsonKey(ignore: true) 1439 | _$$_VersionCopyWith<_$_Version> get copyWith => 1440 | throw _privateConstructorUsedError; 1441 | } 1442 | 1443 | Mod _$ModFromJson(Map json) { 1444 | return _Mod.fromJson(json); 1445 | } 1446 | 1447 | /// @nodoc 1448 | mixin _$Mod { 1449 | /// Mod id. 1450 | String get modid => throw _privateConstructorUsedError; 1451 | 1452 | /// Mod version. 1453 | String get version => throw _privateConstructorUsedError; 1454 | 1455 | Map toJson() => throw _privateConstructorUsedError; 1456 | @JsonKey(ignore: true) 1457 | $ModCopyWith get copyWith => throw _privateConstructorUsedError; 1458 | } 1459 | 1460 | /// @nodoc 1461 | abstract class $ModCopyWith<$Res> { 1462 | factory $ModCopyWith(Mod value, $Res Function(Mod) then) = 1463 | _$ModCopyWithImpl<$Res>; 1464 | $Res call({String modid, String version}); 1465 | } 1466 | 1467 | /// @nodoc 1468 | class _$ModCopyWithImpl<$Res> implements $ModCopyWith<$Res> { 1469 | _$ModCopyWithImpl(this._value, this._then); 1470 | 1471 | final Mod _value; 1472 | // ignore: unused_field 1473 | final $Res Function(Mod) _then; 1474 | 1475 | @override 1476 | $Res call({ 1477 | Object? modid = freezed, 1478 | Object? version = freezed, 1479 | }) { 1480 | return _then(_value.copyWith( 1481 | modid: modid == freezed 1482 | ? _value.modid 1483 | : modid // ignore: cast_nullable_to_non_nullable 1484 | as String, 1485 | version: version == freezed 1486 | ? _value.version 1487 | : version // ignore: cast_nullable_to_non_nullable 1488 | as String, 1489 | )); 1490 | } 1491 | } 1492 | 1493 | /// @nodoc 1494 | abstract class _$$_ModCopyWith<$Res> implements $ModCopyWith<$Res> { 1495 | factory _$$_ModCopyWith(_$_Mod value, $Res Function(_$_Mod) then) = 1496 | __$$_ModCopyWithImpl<$Res>; 1497 | @override 1498 | $Res call({String modid, String version}); 1499 | } 1500 | 1501 | /// @nodoc 1502 | class __$$_ModCopyWithImpl<$Res> extends _$ModCopyWithImpl<$Res> 1503 | implements _$$_ModCopyWith<$Res> { 1504 | __$$_ModCopyWithImpl(_$_Mod _value, $Res Function(_$_Mod) _then) 1505 | : super(_value, (v) => _then(v as _$_Mod)); 1506 | 1507 | @override 1508 | _$_Mod get _value => super._value as _$_Mod; 1509 | 1510 | @override 1511 | $Res call({ 1512 | Object? modid = freezed, 1513 | Object? version = freezed, 1514 | }) { 1515 | return _then(_$_Mod( 1516 | modid: modid == freezed 1517 | ? _value.modid 1518 | : modid // ignore: cast_nullable_to_non_nullable 1519 | as String, 1520 | version: version == freezed 1521 | ? _value.version 1522 | : version // ignore: cast_nullable_to_non_nullable 1523 | as String, 1524 | )); 1525 | } 1526 | } 1527 | 1528 | /// @nodoc 1529 | @JsonSerializable() 1530 | class _$_Mod implements _Mod { 1531 | const _$_Mod({required this.modid, required this.version}); 1532 | 1533 | factory _$_Mod.fromJson(Map json) => _$$_ModFromJson(json); 1534 | 1535 | /// Mod id. 1536 | @override 1537 | final String modid; 1538 | 1539 | /// Mod version. 1540 | @override 1541 | final String version; 1542 | 1543 | @override 1544 | String toString() { 1545 | return 'Mod(modid: $modid, version: $version)'; 1546 | } 1547 | 1548 | @override 1549 | bool operator ==(dynamic other) { 1550 | return identical(this, other) || 1551 | (other.runtimeType == runtimeType && 1552 | other is _$_Mod && 1553 | const DeepCollectionEquality().equals(other.modid, modid) && 1554 | const DeepCollectionEquality().equals(other.version, version)); 1555 | } 1556 | 1557 | @JsonKey(ignore: true) 1558 | @override 1559 | int get hashCode => Object.hash( 1560 | runtimeType, 1561 | const DeepCollectionEquality().hash(modid), 1562 | const DeepCollectionEquality().hash(version)); 1563 | 1564 | @JsonKey(ignore: true) 1565 | @override 1566 | _$$_ModCopyWith<_$_Mod> get copyWith => 1567 | __$$_ModCopyWithImpl<_$_Mod>(this, _$identity); 1568 | 1569 | @override 1570 | Map toJson() { 1571 | return _$$_ModToJson(this); 1572 | } 1573 | } 1574 | 1575 | abstract class _Mod implements Mod { 1576 | const factory _Mod( 1577 | {required final String modid, required final String version}) = _$_Mod; 1578 | 1579 | factory _Mod.fromJson(Map json) = _$_Mod.fromJson; 1580 | 1581 | @override 1582 | 1583 | /// Mod id. 1584 | String get modid => throw _privateConstructorUsedError; 1585 | @override 1586 | 1587 | /// Mod version. 1588 | String get version => throw _privateConstructorUsedError; 1589 | @override 1590 | @JsonKey(ignore: true) 1591 | _$$_ModCopyWith<_$_Mod> get copyWith => throw _privateConstructorUsedError; 1592 | } 1593 | --------------------------------------------------------------------------------