1+ use std:: net:: Ipv4Addr ;
2+ use std:: time:: { Duration , Instant } ;
3+ use colored_text:: Colorize ;
4+ use futures:: { stream, StreamExt } ;
5+ use crate :: database:: { parse_server, ServerHistory , ServerInfo } ;
6+ use crate :: config:: MainConfig ;
7+ use crate :: logger;
8+ use crate :: logger:: DefaultColor ;
9+ use crate :: manager:: TaskManager ;
10+ use crate :: randomizer:: IpGenerator ;
11+ use crate :: scanning:: scanner:: { scan, ScanConfig } ;
12+ use crate :: scanning:: utils:: { format_time, prettier_ping_result, save_server} ;
13+
14+ pub async fn crawl ( gen_config : IpGenerator ) {
15+ let _ = TaskManager :: spawn ( "Crawler" , move |_cancel_token| async move {
16+ loop {
17+ let start_time = Instant :: now ( ) ;
18+
19+ let ports = vec ! [ 25565 , 25566 ] ; // Example for later implementation
20+ let targets: Vec < ( Ipv4Addr , u16 ) > = gen_config. generate ( )
21+ . flat_map ( |ip| {
22+ stream:: iter ( ports. clone ( ) . into_iter ( ) . map ( move |port| ( ip, port) ) )
23+ } )
24+ . collect ( )
25+ . await ;
26+
27+ let total_targets = targets. len ( ) ;
28+ let mut found_batch: Vec < ( ServerInfo , ServerHistory ) > = Vec :: new ( ) ;
29+
30+ let mut total_found_count = 0 ;
31+ let mut processed_count = 0 ;
32+
33+ logger:: info ( format ! (
34+ "Scanning {} targets..." ,
35+ targets. len( ) . hex( DefaultColor :: Highlight . hex( ) )
36+ ) ) . prefix ( "Crawler" ) . send ( ) . await ;
37+
38+ let main_cfg = MainConfig :: get ( ) . expect ( "Config not loaded!" ) ;
39+
40+ let config = ScanConfig {
41+ ping_timeout : Duration :: from_millis ( main_cfg. general . ping_timeout ) ,
42+ query_timeout : Duration :: from_millis ( main_cfg. general . query_timeout ) ,
43+ join_timeout : Duration :: from_millis ( main_cfg. general . join_timeout ) ,
44+ with_uuid : main_cfg. general . do_uuid_fetch ,
45+ max_tasks : main_cfg. get_crawler_tasks ( ) ,
46+ ..ScanConfig :: default ( )
47+ } ;
48+
49+ // Core part: scanning
50+ let scan_stream = scan ( targets, config) ;
51+ tokio:: pin!( scan_stream) ;
52+
53+ // Scan stream
54+ while let Some ( maybe_result) = scan_stream. next ( ) . await {
55+ processed_count += 1 ;
56+
57+ // Success
58+ if let Some ( result) = maybe_result {
59+ let parsed = parse_server ( result. ip , result. port , result. ping . clone ( ) , result. query , result. join ) ;
60+ found_batch. push ( parsed) ;
61+ total_found_count += 1 ;
62+
63+ let mut output = String :: new ( ) ;
64+ output. push_str (
65+ & format ! (
66+ "Found server: {}:{}\n " ,
67+ result. ip. to_string( ) . hex( DefaultColor :: Highlight . hex( ) ) ,
68+ result. port. hex( DefaultColor :: Highlight . hex( ) )
69+ )
70+ ) ;
71+ output. push_str ( & prettier_ping_result ( result. ping ) . await ) ;
72+ logger:: success ( output) . prefix ( "Crawler" ) . send ( ) . await ;
73+ }
74+
75+ // Database insert
76+ if processed_count % 10000 == 0 {
77+ let batch_to_insert = std:: mem:: take ( & mut found_batch) ;
78+ save_server ( & batch_to_insert) . await ;
79+ }
80+
81+ // Progress calc
82+ let elapsed = start_time. elapsed ( ) . as_secs_f64 ( ) ;
83+ let ips_per_second = processed_count as f64 / elapsed;
84+
85+ if ips_per_second > 0.0 {
86+ let remaining_ips = total_targets. saturating_sub ( processed_count) ;
87+ let remaining_secs = remaining_ips as f64 / ips_per_second;
88+ let percent = format ! ( "{:.2}" , ( processed_count as f64 / total_targets as f64 ) * 100.0 ) ;
89+
90+ if processed_count % 10000 == 0 || processed_count == total_targets {
91+ logger:: info ( format ! (
92+ "Progress: {}/{} IPs ({}%) - ETA: {}" ,
93+ processed_count. hex( DefaultColor :: Highlight . hex( ) ) ,
94+ total_targets. hex( DefaultColor :: Highlight . hex( ) ) ,
95+ percent. hex( DefaultColor :: Highlight . hex( ) ) ,
96+ format_time( remaining_secs as u64 )
97+ ) ) . prefix ( "Crawler" ) . send ( ) . await ;
98+ }
99+ }
100+ }
101+
102+ if !found_batch. is_empty ( ) {
103+ save_server ( & found_batch) . await ;
104+ }
105+
106+ // Finished
107+ let elapsed_time = start_time. elapsed ( ) ;
108+
109+ let pps = if elapsed_time. as_secs ( ) > 0 {
110+ total_targets as f64 / elapsed_time. as_secs_f64 ( )
111+ } else {
112+ 0.0
113+ } ;
114+ let percent = format ! ( "{:.2}" , ( processed_count as f64 / total_targets as f64 ) * 100.0 ) ;
115+
116+ logger:: info (
117+ format ! (
118+ "Crawl iteration finished in {}. Found {} servers from {} targets. That is {}% ({}{})" ,
119+ format_time( elapsed_time. as_secs( ) ) . hex( DefaultColor :: Highlight . hex( ) ) ,
120+ total_found_count. hex( DefaultColor :: Highlight . hex( ) ) ,
121+ total_targets. hex( DefaultColor :: Highlight . hex( ) ) ,
122+ percent. hex( DefaultColor :: Highlight . hex( ) ) ,
123+ pps. round( ) . hex( DefaultColor :: Highlight . hex( ) ) ,
124+ "pps" . hex( DefaultColor :: DarkHighlight . hex( ) )
125+ )
126+ ) . send ( ) . await ;
127+ }
128+ } ) . await ;
129+ }
0 commit comments