1
// This file is part of hnefatafl-copenhagen.
2
//
3
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU Affero General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// hnefatafl-copenhagen is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU Affero General Public License for more details.
12
//
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15

            
16
#![deny(clippy::expect_used)]
17
#![deny(clippy::indexing_slicing)]
18
#![deny(clippy::panic)]
19
#![deny(clippy::unwrap_used)]
20

            
21
mod accounts;
22
mod command_line;
23
mod remove_connection;
24
mod smtp;
25
mod tests;
26
mod unix_timestamp;
27

            
28
use std::{
29
    collections::{HashMap, HashSet, VecDeque},
30
    fmt,
31
    fs::{self, File, OpenOptions},
32
    io::{BufRead, BufReader, ErrorKind, Read, Write},
33
    net::{TcpListener, TcpStream},
34
    process::exit,
35
    str::FromStr,
36
    sync::{
37
        Arc, Mutex,
38
        mpsc::{self, Receiver, Sender},
39
    },
40
    thread::{self, sleep},
41
    time::Duration,
42
};
43

            
44
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
45
use chrono::{DateTime, Days, Local, Utc};
46
use clap::{CommandFactory, Parser};
47
use hnefatafl_copenhagen::{
48
    COPYRIGHT, Id, SERVER_PORT, VERSION_ID,
49
    board::BoardSize,
50
    draw::Draw,
51
    email::Email,
52
    game::TimeUnix,
53
    glicko::Outcome,
54
    rating::Rated,
55
    role::Role,
56
    server_game::{
57
        ArchivedGame, Challenger, Messenger, ServerGame, ServerGameLight, ServerGameSerialized,
58
        ServerGames, ServerGamesLight,
59
    },
60
    status::Status,
61
    time::{Time, TimeEnum, TimeSettings},
62
    tournament::{self, Player, Players, Tournament, TournamentTree, Wins},
63
    utils::{self, create_data_folder, data_file},
64
};
65
use lettre::{
66
    SmtpTransport, Transport,
67
    message::{Mailbox, header::ContentType},
68
    transport::smtp::authentication::Credentials,
69
};
70
use log::{debug, error, info, trace};
71
use old_rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
72
use password_hash::SaltString;
73
use rand::random;
74
use serde::{Deserialize, Serialize};
75
use std::fmt::Write as _;
76

            
77
use crate::{
78
    accounts::{Account, Accounts},
79
    command_line::Args,
80
    remove_connection::RemoveConnection,
81
    smtp::Smtp,
82
    unix_timestamp::UnixTimestamp,
83
};
84

            
85
const ACTIVE_GAMES_FILE: &str = "active-games.postcard";
86
const ARCHIVED_GAMES_FILE: &str = "archived-games.ron";
87
/// Seconds in two months: `60.0 * 60.0 * 24.0 * 30.417 * 2.0 = 5_256_057.6`
88
const TWO_MONTHS: i64 = 5_256_058;
89
const SEVEN_DAYS: i64 = 1_000 * 60 * 60 * 24 * 7;
90
const USERS_FILE: &str = "users.ron";
91
const MESSAGE_FILE: &str = "message.txt";
92

            
93
#[allow(clippy::too_many_lines)]
94
fn main() -> anyhow::Result<()> {
95
    // println!("{:x}", rand::random::<u32>());
96
    // return Ok(());
97

            
98
    let args = Args::parse();
99
    utils::init_logger("hnefatafl_server_full", args.debug, args.systemd);
100

            
101
    if args.man {
102
        let mut buffer: Vec<u8> = Vec::default();
103
        let cmd = Args::command()
104
            .name("hnefatafl-server-full")
105
            .long_version(None);
106
        let man = clap_mangen::Man::new(cmd).date("2025-06-23");
107

            
108
        man.render(&mut buffer)?;
109
        write!(buffer, "{COPYRIGHT}")?;
110

            
111
        std::fs::write("hnefatafl-server-full.1", buffer)?;
112
        return Ok(());
113
    }
114

            
115
    create_data_folder()?;
116

            
117
    let (tx, rx) = mpsc::channel();
118
    let mut server = Server {
119
        tx: Some(tx.clone()),
120
        ..Server::default()
121
    };
122

            
123
    if !args.skip_the_data_file {
124
        let users_file = data_file(USERS_FILE);
125
        match &fs::read_to_string(&users_file) {
126
            Ok(string) => match ron::from_str(string.as_str()) {
127
                Ok(server_ron) => {
128
                    server = server_ron;
129
                    server.tx = Some(tx.clone());
130
                }
131
                Err(err) => {
132
                    return Err(anyhow::Error::msg(format!(
133
                        "RON: {}: {err}",
134
                        users_file.display(),
135
                    )));
136
                }
137
            },
138
            Err(err) => match err.kind() {
139
                ErrorKind::NotFound => {}
140
                _ => return Err(anyhow::Error::msg(err.to_string())),
141
            },
142
        }
143

            
144
        let archived_games_file = data_file(ARCHIVED_GAMES_FILE);
145
        match fs::read_to_string(&archived_games_file) {
146
            Ok(archived_games_string) => {
147
                let mut archived_games = Vec::new();
148

            
149
                for line in archived_games_string.lines() {
150
                    let archived_game: ArchivedGame = match ron::from_str(line) {
151
                        Ok(archived_game) => archived_game,
152
                        Err(err) => {
153
                            return Err(anyhow::Error::msg(format!(
154
                                "RON: {}: {err}",
155
                                archived_games_file.display(),
156
                            )));
157
                        }
158
                    };
159
                    archived_games.push(archived_game);
160
                }
161

            
162
                server.archived_games = archived_games;
163
            }
164
            Err(err) => {
165
                error!("archived games file not found: {err}");
166
            }
167
        }
168

            
169
        let active_games_file = data_file(ACTIVE_GAMES_FILE);
170
        if fs::exists(&active_games_file)? {
171
            let mut file = File::open(active_games_file)?;
172
            let mut data = Vec::new();
173
            file.read_to_end(&mut data)?;
174

            
175
            let games: Vec<ServerGameSerialized> = postcard::from_bytes(data.as_slice())?;
176
            for game in games {
177
                let id = game.id;
178
                let server_game_light = ServerGameLight::from(&game);
179
                let server_game = ServerGame::from(game);
180

            
181
                server.games_light.0.insert(id, server_game_light);
182
                server.games.0.insert(id, server_game);
183
            }
184
        }
185

            
186
        let tx_signals = tx.clone();
187
        ctrlc::set_handler(move || {
188
            if !args.systemd {
189
                println!();
190
            }
191
            handle_error(tx_signals.send(("0 server exit".to_string(), None)));
192
        })?;
193
    }
194

            
195
    if args.skip_the_data_file {
196
        server.skip_the_data_file = true;
197
    }
198

            
199
    thread::spawn(move || handle_error(server.handle_messages(&rx)));
200

            
201
    if !args.skip_advertising_updates {
202
        let tx_messages_1 = tx.clone();
203
        thread::spawn(move || {
204
            loop {
205
                handle_error(tx_messages_1.send(("0 server display_server".to_string(), None)));
206
                thread::sleep(Duration::from_secs(1));
207
            }
208
        });
209
    }
210

            
211
    let tx_messages_2 = tx.clone();
212
    thread::spawn(move || {
213
        loop {
214
            handle_error(tx_messages_2.send(("0 server check_update_rd".to_string(), None)));
215
            thread::sleep(Duration::from_secs(60 * 60 * 24));
216
        }
217
    });
218

            
219
    let tx_messages_3 = tx.clone();
220
    thread::spawn(move || {
221
        handle_error(tx_messages_3.send(("0 server tournament_start".to_string(), None)));
222

            
223
        loop {
224
            let now_utc = Utc::now();
225

            
226
            let tomorrow_midnight_utc = (now_utc + Days::new(1))
227
                .date_naive()
228
                .and_hms_opt(0, 0, 0)
229
                .unwrap_or_else(|| {
230
                    error!("and_hms_opt failed");
231
                    exit(1)
232
                })
233
                .and_local_timezone(Utc)
234
                .single()
235
                .unwrap_or_else(|| {
236
                    error!("single failed");
237
                    exit(1)
238
                });
239

            
240
            let duration_until_midnight = tomorrow_midnight_utc.signed_duration_since(now_utc);
241
            debug!(
242
                "seconds until midnight UTC: {}",
243
                duration_until_midnight.num_seconds()
244
            );
245

            
246
            let std_duration = duration_until_midnight.to_std().unwrap_or_else(|error| {
247
                error!("to_std failed: {error}");
248
                exit(1)
249
            });
250

            
251
            sleep(std_duration);
252
            sleep(Duration::from_secs(1));
253

            
254
            handle_error(tx_messages_3.send(("0 server tournament_start".to_string(), None)));
255
        }
256
    });
257

            
258
    let mut address = "[::]".to_string();
259
    address.push_str(SERVER_PORT);
260

            
261
    let listener = match TcpListener::bind(&address) {
262
        Ok(listener) => listener,
263
        Err(error) => {
264
            error!("TcpLister::bind: {error}");
265

            
266
            address = "0.0.0.0".to_string();
267
            address.push_str(SERVER_PORT);
268
            TcpListener::bind(&address)?
269
        }
270
    };
271

            
272
    info!("listening on {address} ...");
273

            
274
    for (index, stream) in (1..).zip(listener.incoming()) {
275
        let stream = match stream {
276
            Ok(stream) => stream,
277
            Err(error) => {
278
                error!("stream: {error}");
279
                continue;
280
            }
281
        };
282

            
283
        if args.secure {
284
            let peer_address = match stream.peer_addr() {
285
                Ok(peer_address) => peer_address.ip(),
286
                Err(error) => {
287
                    error!("peer_address: {error}");
288
                    continue;
289
                }
290
            };
291

            
292
            let (tx_close, rx_close) = mpsc::channel();
293
            tx.send((
294
                format!("0 server connection_add {peer_address}"),
295
                Some(tx_close),
296
            ))?;
297

            
298
            match rx_close.recv() {
299
                Ok(close) => match close.parse() {
300
                    Ok(close) => {
301
                        if close {
302
                            continue;
303
                        }
304
                    }
305
                    Err(error) => {
306
                        error!("close 2: {error}");
307
                        continue;
308
                    }
309
                },
310
                Err(error) => {
311
                    error!("close 1: {error}");
312
                    continue;
313
                }
314
            }
315
        }
316

            
317
        let tx = tx.clone();
318

            
319
        thread::spawn(move || {
320
            if let Err(error) = login(index, stream, &tx) {
321
                error!("login: {error}");
322
            }
323
        });
324
    }
325

            
326
    Ok(())
327
}
328

            
329
#[allow(clippy::too_many_lines)]
330
fn login(
331
    id: Id,
332
    mut stream: TcpStream,
333
    tx: &mpsc::Sender<(String, Option<mpsc::Sender<String>>)>,
334
) -> anyhow::Result<()> {
335
    let args = Args::parse();
336

            
337
    let _remove_connection;
338
    if args.secure {
339
        _remove_connection = RemoveConnection {
340
            address: stream.peer_addr()?.ip(),
341
            tx: tx.clone(),
342
        };
343
    }
344

            
345
    let mut reader = BufReader::new(stream.try_clone()?);
346
    let mut buf = String::new();
347
    let (client_tx, client_rx) = mpsc::channel();
348
    let mut username_proper = "_".to_string();
349
    let mut login_successful = false;
350

            
351
    for _ in 0..100 {
352
        reader.read_line(&mut buf)?;
353

            
354
        for ch in buf.trim().chars() {
355
            if ch.is_control() || ch == '\0' {
356
                return Err(anyhow::Error::msg(
357
                    "there are control characters in the username or password",
358
                ));
359
            }
360
        }
361

            
362
        if buf.trim().is_empty() {
363
            return Err(anyhow::Error::msg(
364
                "The user sent a command without logging in, then quit.",
365
            ));
366
        }
367

            
368
        let buf_clone = buf.clone();
369
        let mut username_password_etc = buf_clone.split_ascii_whitespace();
370

            
371
        let version_id = username_password_etc.next();
372
        let create_account_login = username_password_etc.next();
373
        let username_option = username_password_etc.next();
374

            
375
        if let (Some(version_id), Some(create_account_login), Some(username)) =
376
            (version_id, create_account_login, username_option)
377
        {
378
            username_proper = username.to_string();
379
            if version_id != VERSION_ID {
380
                stream.write_all(b"? login wrong_version\n")?;
381
                buf.clear();
382
                continue;
383
            }
384

            
385
            let password: Vec<&str> = username_password_etc.collect();
386
            let password = password.join(" ");
387

            
388
            if username.len() > 16 {
389
                stream.write_all(b"? login _ username is more than 16 characters\n")?;
390
                buf.clear();
391
                continue;
392
            }
393
            if password.len() > 32 {
394
                stream.write_all(b"? login _ password is more than 32 characters\n")?;
395
                buf.clear();
396
                continue;
397
            }
398

            
399
            debug!("{id} {username} {create_account_login} {password}");
400

            
401
            if create_account_login == "reset_password" {
402
                tx.send((
403
                    format!("0 {username} reset_password"),
404
                    Some(client_tx.clone()),
405
                ))?;
406

            
407
                stream.write_all(b"? login reset_password\n")?;
408

            
409
                buf.clear();
410
                continue;
411
            }
412

            
413
            tx.send((
414
                format!("{id} {username} {create_account_login} {password}"),
415
                Some(client_tx.clone()),
416
            ))?;
417

            
418
            let message = client_rx.recv()?;
419
            buf.clear();
420
            if create_account_login == "login" {
421
                if "= login" == message.as_str() {
422
                    login_successful = true;
423
                    break;
424
                }
425

            
426
                stream.write_all(b"? login multiple_possible_errors\n")?;
427
                continue;
428
            } else if create_account_login == "create_account" {
429
                if "= create_account" == message.as_str() {
430
                    login_successful = true;
431
                    break;
432
                }
433

            
434
                stream.write_all(b"? create_account\n")?;
435
                continue;
436
            }
437

            
438
            stream.write_all(b"? login _\n")?;
439
        }
440

            
441
        buf.clear();
442
    }
443

            
444
    if !login_successful {
445
        return Err(anyhow::Error::msg("the user failed to login"));
446
    }
447
    stream.write_all(b"= login\n")?;
448

            
449
    thread::spawn(move || {
450
        if let Err(error) = receiving_and_writing(stream, &client_rx) {
451
            error!("receiving_and_writing: {error}");
452
        }
453
    });
454

            
455
    tx.send((format!("{id} {username_proper} email_get"), None))?;
456
    tx.send((format!("{id} {username_proper} texts"), None))?;
457
    tx.send((format!("{id} {username_proper} message"), None))?;
458
    tx.send((format!("{id} {username_proper} display_games"), None))?;
459
    tx.send((format!("{id} {username_proper} tournament_status"), None))?;
460
    tx.send((format!("{id} {username_proper} admin"), None))?;
461

            
462
    'outer: for _ in 0..1_000_000 {
463
        if let Err(err) = reader.read_line(&mut buf) {
464
            error!("reader.read_line(): {err}");
465
            break 'outer;
466
        }
467

            
468
        let buf_str = buf.trim();
469

            
470
        if buf_str.is_empty() {
471
            break 'outer;
472
        }
473

            
474
        for char in buf_str.chars() {
475
            if char.is_control() || char == '\0' {
476
                break 'outer;
477
            }
478
        }
479

            
480
        tx.send((format!("{id} {username_proper} {buf_str}"), None))?;
481
        buf.clear();
482
    }
483

            
484
    tx.send((format!("{id} {username_proper} logout"), None))?;
485
    Ok(())
486
}
487

            
488
fn receiving_and_writing<T: Send + Write>(
489
    mut stream: T,
490
    client_rx: &Receiver<String>,
491
) -> anyhow::Result<()> {
492
    for mut message in client_rx {
493
        match message.as_str() {
494
            "= archived_games" => {
495
                let ron_archived_games = client_rx.recv()?;
496
                let archived_games: Vec<ArchivedGame> = ron::from_str(&ron_archived_games)?;
497
                let postcard_archived_games = &postcard::to_allocvec(&archived_games)?;
498

            
499
                writeln!(message, " {}", postcard_archived_games.len())?;
500
                stream.write_all(message.as_bytes())?;
501
                stream.write_all(postcard_archived_games)?;
502
            }
503
            "= logout" => return Ok(()),
504
            _ => {
505
                message.push('\n');
506
                if let Err(error) = stream.write_all(message.as_bytes()) {
507
                    return Err(anyhow::Error::msg(format!("{message}: {error}")));
508
                }
509
            }
510
        }
511
    }
512

            
513
    Ok(())
514
}
515

            
516
fn generate_round_one(players: Vec<Player>) -> Vec<tournament::Status> {
517
    let players_len = players.len();
518

            
519
    if players_len == 1
520
        && let Some(player) = players.first()
521
    {
522
        return vec![tournament::Status::Won(player.clone())];
523
    }
524

            
525
    let mut power = 1;
526
    while power < players_len {
527
        power *= 2;
528
    }
529

            
530
    let mut tournament_players = VecDeque::new();
531
    for player in players {
532
        tournament_players.push_front(tournament::Status::Ready(player));
533
    }
534
    for _ in 0..(power - players_len) {
535
        tournament_players.push_back(tournament::Status::None);
536
    }
537

            
538
    let mut round = Vec::new();
539
    for i in 0..tournament_players.len() {
540
        if i % 2 == 0 {
541
            let Some(player) = tournament_players.pop_back() else {
542
                unreachable!()
543
            };
544

            
545
            round.push(player);
546
        } else {
547
            let Some(player) = tournament_players.pop_front() else {
548
                unreachable!()
549
            };
550

            
551
            round.push(player);
552
        }
553
    }
554

            
555
    let mut round_new = Vec::new();
556
    for statuses in round.chunks(2) {
557
        let (Some(status_1), Some(status_2)) = (statuses.first(), statuses.get(1)) else {
558
            return round_new;
559
        };
560

            
561
        let status_1 = status_1.clone();
562
        let status_2 = status_2.clone();
563

            
564
        let (status_1, status_2) = match (status_1, status_2) {
565
            (tournament::Status::Ready(player), tournament::Status::None) => (
566
                tournament::Status::Won(player.clone()),
567
                tournament::Status::None,
568
            ),
569
            (tournament::Status::None, tournament::Status::Ready(player)) => (
570
                tournament::Status::None,
571
                tournament::Status::Won(player.clone()),
572
            ),
573
            (status_1, status_2) => (status_1, status_2),
574
        };
575

            
576
        round_new.push(status_1);
577
        round_new.push(status_2);
578
    }
579

            
580
    round = round_new;
581
    round
582
}
583

            
584
fn handle_error<T, E: fmt::Display>(result: Result<T, E>) -> T {
585
    match result {
586
        Ok(value) => value,
587
        Err(error) => {
588
            error!("{error}");
589
            exit(1)
590
        }
591
    }
592
}
593

            
594
fn hash_password(password: &str) -> Option<String> {
595
    let ctx = Argon2::default();
596
    let salt = SaltString::generate(&mut OsRng);
597
    Some(
598
        ctx.hash_password(password.as_bytes(), &salt)
599
            .ok()?
600
            .to_string(),
601
    )
602
}
603

            
604
fn timestamp() -> String {
605
    Utc::now().format("[%F %T UTC]").to_string()
606
}
607

            
608
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
609
struct Server {
610
    #[serde(default)]
611
    game_id: Id,
612
    #[serde(default)]
613
    ran_update_rd: UnixTimestamp,
614
    #[serde(default)]
615
    admins: HashSet<String>,
616
    #[serde(default)]
617
    smtp: Smtp,
618
    #[serde(default)]
619
    tournament: Option<Tournament>,
620
    #[serde(default)]
621
    accounts: Accounts,
622
    #[serde(skip)]
623
    accounts_old: Accounts,
624
    #[serde(skip)]
625
    archived_games: Vec<ArchivedGame>,
626
    #[serde(skip)]
627
    clients: HashMap<usize, mpsc::Sender<String>>,
628
    #[serde(skip)]
629
    connections: HashMap<String, u128>,
630
    #[serde(skip)]
631
    games: ServerGames,
632
    #[serde(skip)]
633
    games_light: ServerGamesLight,
634
    #[serde(skip)]
635
    games_light_old: ServerGamesLight,
636
    #[serde(skip)]
637
    skip_the_data_file: bool,
638
    #[serde(skip)]
639
    texts: VecDeque<String>,
640
    #[serde(skip)]
641
    tx: Option<mpsc::Sender<(String, Option<mpsc::Sender<String>>)>>,
642
}
643

            
644
impl Server {
645
    fn append_archived_game(&mut self, game: ServerGame) -> anyhow::Result<()> {
646
        let Some(attacker) = self.accounts.0.get(&game.attacker) else {
647
            return Err(anyhow::Error::msg("failed to get rating!"));
648
        };
649
        let Some(defender) = self.accounts.0.get(&game.defender) else {
650
            return Err(anyhow::Error::msg("failed to get rating!"));
651
        };
652
        let game = ArchivedGame::new(game, attacker.rating.clone(), defender.rating.clone());
653

            
654
        let archived_games_file = data_file(ARCHIVED_GAMES_FILE);
655
        let mut game_string = ron::ser::to_string(&game)?;
656
        game_string.push('\n');
657

            
658
        let mut file = OpenOptions::new()
659
            .create(true)
660
            .append(true)
661
            .open(archived_games_file)?;
662

            
663
        file.write_all(game_string.as_bytes())?;
664

            
665
        self.archived_games.push(game);
666

            
667
        Ok(())
668
    }
669

            
670
    fn bcc_mailboxes(&self, username: &str) -> Vec<Mailbox> {
671
        let mut emails = Vec::new();
672

            
673
        if let Some(account) = self.accounts.0.get(username)
674
            && account.send_emails
675
        {
676
            for account in self.accounts.0.values() {
677
                if let Some(email) = &account.email
678
                    && email.verified
679
                    && let Some(email) = email.to_mailbox()
680
                {
681
                    emails.push(email);
682
                }
683
            }
684
        }
685

            
686
        emails
687
    }
688

            
689
    fn bcc_send(&self, username: &str) -> String {
690
        let mut emails = Vec::new();
691

            
692
        if let Some(account) = self.accounts.0.get(username)
693
            && account.send_emails
694
        {
695
            for account in self.accounts.0.values() {
696
                if let Some(email) = &account.email
697
                    && email.verified
698
                {
699
                    emails.push(email.tx());
700
                }
701
            }
702
        }
703

            
704
        emails.sort();
705
        emails.join(" ")
706
    }
707

            
708
    /// ```sh
709
    /// # PASSWORD can be the empty string.
710
    /// <- change_password PASSWORD
711
    /// -> = change_password
712
    /// ```
713
    fn change_password(
714
        &mut self,
715
        username: &str,
716
        index_supplied: usize,
717
        command: &str,
718
        the_rest: &[&str],
719
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
720
        info!("{index_supplied} {username} change_password");
721

            
722
        let account = self.accounts.0.get_mut(username)?;
723
        let password = the_rest.join(" ");
724

            
725
        if password.len() > 32 {
726
            return Some((
727
                self.clients.get(&index_supplied)?.clone(),
728
                false,
729
                format!("{command} password is greater than 32 characters"),
730
            ));
731
        }
732

            
733
        let hash = hash_password(&password)?;
734
        account.password = hash;
735
        self.save_server();
736

            
737
        Some((
738
            self.clients.get(&index_supplied)?.clone(),
739
            true,
740
            (*command).to_string(),
741
        ))
742
    }
743

            
744
    /// ```sh
745
    /// # server internal
746
    /// ```
747
    ///
748
    /// c = 63.2
749
    ///
750
    /// This assumes 30 2 month periods must pass before one's rating
751
    /// deviation is the same as a new player and that a typical RD is 50.
752
    #[must_use]
753
    fn check_update_rd(&mut self) -> bool {
754
        let now = Local::now().to_utc().timestamp();
755
        if now - self.ran_update_rd.0 >= TWO_MONTHS {
756
            for account in self.accounts.0.values_mut() {
757
                account.rating.update_rd();
758
            }
759
            self.ran_update_rd = UnixTimestamp(now);
760
            true
761
        } else {
762
            false
763
        }
764
    }
765

            
766
    /// ```sh
767
    /// # PASSWORD can be the empty string.
768
    /// <- VERSION_ID create_account player-1 PASSWORD
769
    /// -> = login
770
    /// ```
771
    fn create_account(
772
        &mut self,
773
        username: &str,
774
        index_supplied: usize,
775
        command: &str,
776
        the_rest: &[&str],
777
        option_tx: Option<Sender<String>>,
778
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
779
        let password = the_rest.join(" ");
780
        let tx = option_tx?;
781

            
782
        if self.accounts.0.contains_key(username) || username == "server" {
783
            info!("{index_supplied} {username} is already in the database");
784
            Some((tx, false, (*command).to_string()))
785
        } else {
786
            info!("{index_supplied} {username} created user account");
787

            
788
            let hash = hash_password(&password)?;
789
            self.clients.insert(index_supplied, tx);
790
            self.accounts.0.insert(
791
                (*username).to_string(),
792
                Account {
793
                    password: hash,
794
                    logged_in: Some(index_supplied),
795
                    ..Default::default()
796
                },
797
            );
798

            
799
            self.save_server();
800

            
801
            Some((
802
                self.clients.get(&index_supplied)?.clone(),
803
                true,
804
                (*command).to_string(),
805
            ))
806
        }
807
    }
808

            
809
    fn decline_game(
810
        &mut self,
811
        username: &str,
812
        index_supplied: usize,
813
        mut command: String,
814
        the_rest: &[&str],
815
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
816
        let channel = self.clients.get(&index_supplied)?;
817

            
818
        let Some(id) = the_rest.first() else {
819
            return Some((channel.clone(), false, command));
820
        };
821
        let Ok(id) = id.parse::<Id>() else {
822
            return Some((channel.clone(), false, command));
823
        };
824

            
825
        let mut switch = false;
826
        if let Some(&"switch") = the_rest.get(1) {
827
            switch = true;
828
        }
829

            
830
        info!("{index_supplied} {username} decline_game {id} switch={switch}");
831

            
832
        if let Some(game_old) = self.games_light.0.remove(&id) {
833
            let mut attacker = None;
834
            let mut attacker_channel = None;
835
            let mut defender = None;
836
            let mut defender_channel = None;
837

            
838
            if switch {
839
                if Some(username.to_string()) == game_old.attacker {
840
                    defender = game_old.defender;
841
                    defender_channel = game_old.defender_channel;
842
                } else if Some(username.to_string()) == game_old.defender {
843
                    attacker = game_old.attacker;
844
                    attacker_channel = game_old.attacker_channel;
845
                }
846
            } else if Some(username.to_string()) == game_old.attacker {
847
                attacker = game_old.attacker;
848
                attacker_channel = game_old.attacker_channel;
849
            } else if Some(username.to_string()) == game_old.defender {
850
                defender = game_old.defender;
851
                defender_channel = game_old.defender_channel;
852
            }
853

            
854
            let game = ServerGameLight {
855
                id,
856
                attacker,
857
                defender,
858
                challenger: Challenger::default(),
859
                rated: game_old.rated,
860
                timed: game_old.timed,
861
                board_size: game_old.board_size,
862
                attacker_channel,
863
                defender_channel,
864
                spectators: game_old.spectators,
865
                challenge_accepted: false,
866
                game_over: false,
867
            };
868

            
869
            command = format!("{command} {game:?}");
870
            self.games_light.0.insert(id, game);
871
        }
872

            
873
        Some((channel.clone(), true, command))
874
    }
875

            
876
    fn delete_account(&mut self, username: &str, index_supplied: usize) {
877
        info!("{index_supplied} {username} delete_account");
878

            
879
        self.accounts.0.remove(username);
880
        self.save_server();
881
    }
882

            
883
    fn display_server(&mut self, username: &str) -> Option<(mpsc::Sender<String>, bool, String)> {
884
        if self.games_light != self.games_light_old {
885
            debug!("0 {username} display_games");
886
            self.games_light_old = self.games_light.clone();
887

            
888
            for tx in &mut self.clients.values() {
889
                let _ok = tx.send(format!("= display_games {:?}", &self.games_light));
890
            }
891
        }
892

            
893
        if self.accounts != self.accounts_old {
894
            debug!("0 {username} display_users");
895
            self.accounts_old = self.accounts.clone();
896

            
897
            for tx in &mut self.clients.values() {
898
                let _ok = tx.send(format!("= display_users {}", &self.accounts));
899
            }
900
        }
901

            
902
        for game in self.games.0.values_mut() {
903
            match game.game.turn {
904
                Role::Attacker => {
905
                    if game.game.status == Status::Ongoing
906
                        && let TimeUnix::Time(game_time) = &mut game.game.time
907
                    {
908
                        let now = Local::now().to_utc().timestamp_millis();
909
                        let elapsed_time = now - *game_time;
910
                        game.elapsed_time += elapsed_time;
911
                        *game_time = now;
912

            
913
                        if game.elapsed_time > SEVEN_DAYS
914
                            && let Some(tx) = &mut self.tx
915
                        {
916
                            let _ok = tx.send((
917
                                format!(
918
                                    "0 {} game {} play attacker resigns _",
919
                                    game.attacker, game.id
920
                                ),
921
                                None,
922
                            ));
923
                            return None;
924
                        }
925

            
926
                        if let TimeSettings::Timed(attacker_time) = &mut game.game.attacker_time {
927
                            if attacker_time.milliseconds_left > 0 {
928
                                attacker_time.milliseconds_left -= elapsed_time;
929
                            } else if let Some(tx) = &mut self.tx {
930
                                let _ok = tx.send((
931
                                    format!(
932
                                        "0 {} game {} play attacker resigns _",
933
                                        game.attacker, game.id
934
                                    ),
935
                                    None,
936
                                ));
937
                            }
938
                        }
939
                    }
940
                }
941
                Role::Roleless => {}
942
                Role::Defender => {
943
                    if game.game.status == Status::Ongoing
944
                        && let TimeUnix::Time(game_time) = &mut game.game.time
945
                    {
946
                        let now = Local::now().to_utc().timestamp_millis();
947
                        let elapsed_time = now - *game_time;
948
                        game.elapsed_time += elapsed_time;
949
                        *game_time = now;
950

            
951
                        if game.elapsed_time > SEVEN_DAYS
952
                            && let Some(tx) = &mut self.tx
953
                        {
954
                            let _ok = tx.send((
955
                                format!(
956
                                    "0 {} game {} play defender resigns _",
957
                                    game.defender, game.id
958
                                ),
959
                                None,
960
                            ));
961
                            return None;
962
                        }
963

            
964
                        if let TimeSettings::Timed(defender_time) = &mut game.game.defender_time {
965
                            if defender_time.milliseconds_left > 0 {
966
                                defender_time.milliseconds_left -= elapsed_time;
967
                            } else if let Some(tx) = &mut self.tx {
968
                                let _ok = tx.send((
969
                                    format!(
970
                                        "0 {} game {} play defender resigns _",
971
                                        game.defender, game.id
972
                                    ),
973
                                    None,
974
                                ));
975
                            }
976
                        }
977
                    }
978
                }
979
            }
980
        }
981

            
982
        None
983
    }
984

            
985
    fn draw(
986
        &mut self,
987
        index_supplied: usize,
988
        command: &str,
989
        the_rest: &[&str],
990
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
991
        let Some(id) = the_rest.first() else {
992
            return Some((
993
                self.clients.get(&index_supplied)?.clone(),
994
                false,
995
                (*command).to_string(),
996
            ));
997
        };
998
        let Ok(id) = id.parse::<Id>() else {
999
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(draw) = the_rest.get(1) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Ok(draw) = Draw::from_str(draw) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(mut game) = self.games.0.remove(&id) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let message = format!("= draw {draw}");
        game.attacker_tx.send(message.clone());
        game.defender_tx.send(message.clone());
        if draw == Draw::Accept {
            let Some(game_light) = self.games_light.0.get(&id) else {
                return Some((
                    self.clients.get(&index_supplied)?.clone(),
                    false,
                    (*command).to_string(),
                ));
            };
            for spectator in game_light.spectators.values() {
                if let Some(sender) = self.clients.get(spectator) {
                    let _ok = sender.send(message.clone());
                }
            }
            game.game.status = Status::Draw;
            let accounts = &mut self.accounts.0;
            let (attacker_rating, defender_rating) = if let (Some(attacker), Some(defender)) =
                (accounts.get(&game.attacker), accounts.get(&game.defender))
            {
                (attacker.rating.rating, defender.rating.rating)
            } else {
                unreachable!();
            };
            if let Some(attacker) = accounts.get_mut(&game.attacker) {
                attacker.draws += 1;
                if game.rated.into() {
                    attacker
                        .rating
                        .update_rating(defender_rating, &Outcome::Draw);
                }
            }
            if let Some(defender) = accounts.get_mut(&game.defender) {
                defender.draws += 1;
                if game.rated.into() {
                    defender
                        .rating
                        .update_rating(attacker_rating, &Outcome::Draw);
                }
            }
            if let Some(game) = self.games_light.0.get_mut(&id) {
                game.game_over = true;
            }
            if !self.skip_the_data_file {
                self.append_archived_game(game)
                    .map_err(|err| {
                        error!("append_archived_games: {err}");
                    })
                    .ok()?;
            }
            self.save_server();
        }
        None
    }
    #[allow(clippy::too_many_lines)]
    fn game(
        &mut self,
        index_supplied: usize,
        username: &str,
        command: &str,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        if the_rest.len() < 5 {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        }
        let index = the_rest.first()?;
        let Ok(index) = index.parse() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let role = the_rest.get(2)?;
        let Ok(role) = Role::from_str(role) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let from = the_rest.get(3)?;
        let to = the_rest.get(4)?;
        let mut to = (*to).to_string();
        if to == "_" {
            to = String::new();
        }
        let Some(game) = self.games.0.get_mut(&index) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(game_light) = self.games_light.0.get_mut(&index) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        game.elapsed_time = 0;
        let mut attackers_turn_next = true;
        if role == Role::Attacker {
            if *username == game.attacker {
                game.game
                    .read_line(&format!("play attacker {from} {to}"))
                    .map_err(|error| {
                        error!("play attacker {from} {to}: {error}");
                        error
                    })
                    .ok()?;
                attackers_turn_next = false;
                let message = format!("game {index} play attacker {from} {to}");
                for spectator in game_light.spectators.values() {
                    if let Some(client) = self.clients.get(spectator) {
                        let _ok = client.send(message.clone());
                    }
                }
                game.defender_tx.send(message);
            } else {
                return Some((
                    self.clients.get(&index_supplied)?.clone(),
                    false,
                    (*command).to_string(),
                ));
            }
        } else if *username == game.defender {
            game.game
                .read_line(&format!("play defender {from} {to}"))
                .map_err(|error| {
                    error!("play defender {from} {to}: {error}");
                    error
                })
                .ok()?;
            let message = format!("game {index} play defender {from} {to}");
            for spectator in game_light.spectators.values() {
                if let Some(client) = self.clients.get(spectator) {
                    let _ok = client.send(message.clone());
                }
            }
            game.attacker_tx.send(message);
        } else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        }
        let mut game_over = false;
        let mut winner_role = Role::Roleless;
        let mut winner_wins = None;
        match game.game.status {
            Status::AttackerWins => {
                let accounts = &mut self.accounts.0;
                let (attacker_rating, defender_rating) = if let (Some(attacker), Some(defender)) =
                    (accounts.get(&game.attacker), accounts.get(&game.defender))
                {
                    (attacker.rating.rating, defender.rating.rating)
                } else {
                    unreachable!();
                };
                if let Some(attacker) = accounts.get_mut(&game.attacker) {
                    attacker.wins += 1;
                    if game.rated.into() {
                        attacker
                            .rating
                            .update_rating(defender_rating, &Outcome::Win);
                    }
                }
                if let Some(defender) = accounts.get_mut(&game.defender) {
                    defender.losses += 1;
                    if game.rated.into() {
                        defender
                            .rating
                            .update_rating(attacker_rating, &Outcome::Loss);
                    }
                }
                let message = format!("= game_over {index} attacker_wins");
                game.attacker_tx.send(message.clone());
                game.defender_tx.send(message.clone());
                for spectator in game_light.spectators.values() {
                    if let Some(sender) = self.clients.get(spectator) {
                        let _ok = sender.send(message.clone());
                    }
                }
                game_over = true;
                winner_role = Role::Attacker;
            }
            Status::Draw => {
                // Handled in the draw fn.
            }
            Status::Ongoing => {
                if attackers_turn_next {
                    game.attacker_tx
                        .send(format!("game {index} generate_move attacker"));
                } else {
                    game.defender_tx
                        .send(format!("game {index} generate_move defender"));
                }
            }
            Status::DefenderWins => {
                let accounts = &mut self.accounts.0;
                let (attacker_rating, defender_rating) = if let (Some(attacker), Some(defender)) =
                    (accounts.get(&game.attacker), accounts.get(&game.defender))
                {
                    (attacker.rating.rating, defender.rating.rating)
                } else {
                    unreachable!();
                };
                if let Some(attacker) = accounts.get_mut(&game.attacker) {
                    attacker.losses += 1;
                    if game.rated.into() {
                        attacker
                            .rating
                            .update_rating(defender_rating, &Outcome::Loss);
                    }
                }
                if let Some(defender) = accounts.get_mut(&game.defender) {
                    defender.wins += 1;
                    if game.rated.into() {
                        defender
                            .rating
                            .update_rating(attacker_rating, &Outcome::Win);
                    }
                }
                let message = format!("= game_over {index} defender_wins");
                game.attacker_tx.send(message.clone());
                game.defender_tx.send(message.clone());
                for spectator in game_light.spectators.values() {
                    if let Some(sender) = self.clients.get(spectator) {
                        let _ok = sender.send(message.clone());
                    }
                }
                game_over = true;
                winner_role = Role::Defender;
            }
        }
        if game_over {
            let mut attacker_defender = None;
            let Some(game) = self.games.0.remove(&index) else {
                unreachable!()
            };
            if let Some(game_light) = self.games_light.0.get_mut(&index) {
                game_light.game_over = true;
            }
            let mut active_game_arc = None;
            let mut tournament_status_update = false;
            if let Some(tournament) = &mut self.tournament
                && let Some(tree) = &mut tournament.tree
                && let Some(active_game) = tree.active_games.get_mut(&game.id)
            {
                tournament_status_update = true;
                active_game_arc = Some(active_game.clone());
                let mut active_game = active_game.lock().ok()?;
                match winner_role {
                    Role::Attacker => {
                        if game.attacker == active_game.player_1.name {
                            active_game.player_1.attacker += 1;
                        } else {
                            active_game.player_2.attacker += 1;
                        }
                    }
                    Role::Defender => {
                        if game.defender == active_game.player_1.name {
                            active_game.player_1.defender += 1;
                        } else {
                            active_game.player_2.defender += 1;
                        }
                    }
                    Role::Roleless => {}
                }
                let player_1_wins = active_game.player_1.attacker + active_game.player_1.defender;
                let player_2_wins = active_game.player_2.attacker + active_game.player_2.defender;
                let total_wins = player_1_wins + player_2_wins;
                let rating_1 = if let Some(account_1) =
                    self.accounts.0.get(active_game.player_1.name.as_str())
                {
                    account_1.rating.rating.round()
                } else {
                    1500.0
                };
                let rating_2 = if let Some(account_2) =
                    self.accounts.0.get(active_game.player_2.name.as_str())
                {
                    account_2.rating.rating.round()
                } else {
                    1500.0
                };
                trace!("total_wins: {total_wins}");
                if total_wins < 2 {
                    // Do nothing.
                } else if total_wins % 2 == 0 || total_wins > 4 {
                    if player_1_wins > player_2_wins {
                        winner_wins = Some(active_game.player_1.clone());
                    } else if player_2_wins > player_1_wins {
                        winner_wins = Some(active_game.player_2.clone());
                    }
                } else if active_game.player_1.attacker > active_game.player_2.attacker {
                    winner_wins = Some(active_game.player_1.clone());
                } else if active_game.player_2.attacker > active_game.player_1.attacker {
                    winner_wins = Some(active_game.player_2.clone());
                }
                trace!(
                    "winner: {winner_wins:#?}, active_game_round: {}",
                    active_game.round
                );
                if let Some(ref mut winner) = winner_wins
                    && let winner_name = winner.name.as_str()
                    && let Some(round) = tree.rounds.get_mut(active_game.round)
                {
                    for (i, statuses) in round.chunks_mut(2).enumerate() {
                        if i == active_game.chunk {
                            let (status_1, status_2) = statuses.split_at_mut(1);
                            let (Some(status_1), Some(status_2)) =
                                (status_1.first_mut(), status_2.first_mut())
                            else {
                                continue;
                            };
                            if winner_name == active_game.player_1.name.as_str() {
                                *status_1 = tournament::Status::Won(Player {
                                    name: active_game.player_1.name.clone(),
                                    rating: rating_1,
                                });
                                *status_2 = tournament::Status::Lost(Player {
                                    name: active_game.player_2.name.clone(),
                                    rating: rating_2,
                                });
                            } else {
                                *status_1 = tournament::Status::Lost(Player {
                                    name: active_game.player_1.name.clone(),
                                    rating: rating_1,
                                });
                                *status_2 = tournament::Status::Won(Player {
                                    name: active_game.player_2.name.clone(),
                                    rating: rating_2,
                                });
                            }
                        }
                    }
                } else if total_wins > 1 {
                    if total_wins % 2 == 0 {
                        // Add a game with the higher rated player as the attacker.
                        let player_1_attacking = if rating_1 > rating_2 {
                            true
                        } else if rating_1 < rating_2 {
                            false
                        } else {
                            random()
                        };
                        if player_1_attacking {
                            attacker_defender =
                                Some((active_game.player_1.clone(), active_game.player_2.clone()));
                        } else {
                            attacker_defender =
                                Some((active_game.player_2.clone(), active_game.player_1.clone()));
                        }
                    } else {
                        // Add the player as the attacker with more total wins.
                        if player_1_wins > player_2_wins {
                            attacker_defender =
                                Some((active_game.player_1.clone(), active_game.player_2.clone()));
                        } else {
                            attacker_defender =
                                Some((active_game.player_2.clone(), active_game.player_1.clone()));
                        }
                    }
                }
            }
            if let Some(tournament) = &mut self.tournament
                && let Some(tree) = &mut tournament.tree
            {
                tree.active_games.remove(&game.id);
            }
            if tournament_status_update {
                self.tournament_update_wins();
                self.tournament_ready_to_playing();
                self.tournament_status_all();
            }
            if !self.skip_the_data_file {
                self.append_archived_game(game)
                    .map_err(|err| {
                        error!("append_archived_game: {err}");
                    })
                    .ok()?;
            }
            trace!(
                "attacker_defender: {attacker_defender:#?}, active_game_arc: {active_game_arc:#?}"
            );
            if let (Some((attacker, defender)), Some(active_game)) =
                (attacker_defender, active_game_arc)
            {
                let id = self.new_tournament_game(&attacker.name, &defender.name);
                if let Some(tournament) = &mut self.tournament
                    && let Some(tree) = &mut tournament.tree
                {
                    tree.active_games.insert(id, active_game);
                }
            }
            self.save_server();
            return None;
        }
        Some((
            self.clients.get(&index_supplied)?.clone(),
            true,
            (*command).to_string(),
        ))
    }
    fn set_email(
        &mut self,
        index_supplied: usize,
        username: &str,
        command: &str,
        email: Option<&str>,
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(address) = email else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(account) = self.accounts.0.get_mut(username) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let random_u32 = random();
        let email = Email {
            address: address.to_string(),
            code: Some(random_u32),
            username: username.to_string(),
            verified: false,
        };
        info!("{index_supplied} {username} email {}", email.tx());
        let email_send = lettre::Message::builder()
            .from("Hnefatafl Org <no-reply@hnefatafl.org>".parse().ok()?)
            .to(email.to_mailbox()?)
            .subject("Account Verification")
            .header(ContentType::TEXT_PLAIN)
            .body(format!(
                "Dear {username},\nyour email verification code is as follows: {random_u32:x}",
            ))
            .ok()?;
        let credentials = Credentials::new(self.smtp.username.clone(), self.smtp.password.clone());
        let mailer = SmtpTransport::relay(&self.smtp.service)
            .ok()?
            .credentials(credentials)
            .build();
        match mailer.send(&email_send) {
            Ok(_) => {
                info!("email sent to {address} successfully!");
                account.email = Some(email);
                self.save_server();
                let reply = format!("email {address} false");
                Some((self.clients.get(&index_supplied)?.clone(), true, reply))
            }
            Err(err) => {
                let reply = format!("could not send email to {address}");
                error!("{reply}: {err}");
                Some((self.clients.get(&index_supplied)?.clone(), false, reply))
            }
        }
    }
    pub(crate) fn handle_messages(
        &mut self,
        rx: &mpsc::Receiver<(String, Option<mpsc::Sender<String>>)>,
    ) -> anyhow::Result<()> {
        loop {
            if let Some((tx, ok, command)) = self.handle_messages_internal(rx) {
                if ok {
                    tx.send(format!("= {command}"))?;
                } else {
                    tx.send(format!("? {command}"))?;
                }
            }
        }
    }
    #[allow(clippy::too_many_lines)]
    fn handle_messages_internal(
        &mut self,
        rx: &mpsc::Receiver<(String, Option<mpsc::Sender<String>>)>,
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let args = Args::parse();
        let (message, option_tx) = rx.recv().ok()?;
        let index_username_command: Vec<_> = message.split_ascii_whitespace().collect();
        if let (Some(index_supplied), Some(username), Some(command)) = (
            index_username_command.first(),
            index_username_command.get(1),
            index_username_command.get(2),
        ) {
            if *command != "check_update_rd"
                && *command != "create_account"
                && *command != "display_server"
                && *command != "login"
                && *command != "ping"
            {
                debug!("{index_supplied} {username} {command}");
            }
            let index_supplied = index_supplied.parse::<usize>().ok()?;
            let the_rest: Vec<_> = index_username_command.clone().into_iter().skip(3).collect();
            match *command {
                "admin" => {
                    if self.admins.contains(*username) {
                        self.clients
                            .get(&index_supplied)?
                            .send("= admin".to_string())
                            .ok()?;
                    }
                    None
                }
                "archived_games" => {
                    self.clients
                        .get(&index_supplied)?
                        .send("= archived_games".to_string())
                        .ok()?;
                    self.clients
                        .get(&index_supplied)?
                        .send(ron::ser::to_string(&self.archived_games).ok()?)
                        .ok()?;
                    None
                }
                "change_password" => {
                    self.change_password(username, index_supplied, command, the_rest.as_slice())
                }
                "check_update_rd" => {
                    let bool = self.check_update_rd();
                    info!("0 {username} check_update_rd {bool}");
                    None
                }
                "connection_add" => {
                    if let Some(address) = the_rest.first()
                        && let Some(tx) = option_tx
                    {
                        if let Some(connections) = self.connections.get(*address)
                            && *connections > 2_000
                        {
                            tx.send("true".to_string()).ok()?;
                        } else {
                            tx.send("false".to_string()).ok()?;
                            let entry = self.connections.entry(address.to_string());
                            entry.and_modify(|value| *value += 1).or_insert(1);
                        }
                    }
                    debug!("connections: {:?}", self.connections);
                    None
                }
                "connection_remove" => {
                    if let Some(connection) = the_rest.first() {
                        let entry = self.connections.entry(connection.to_string());
                        entry.and_modify(|value| *value = value.saturating_sub(1));
                        if let Some(value) = self.connections.get(*connection)
                            && *value == 0
                        {
                            self.connections.remove(*connection);
                        }
                    }
                    debug!("connections: {:?}", self.connections);
                    None
                }
                "create_account" => self.create_account(
                    username,
                    index_supplied,
                    command,
                    the_rest.as_slice(),
                    option_tx,
                ),
                "decline_game" => self.decline_game(
                    username,
                    index_supplied,
                    (*command).to_string(),
                    the_rest.as_slice(),
                ),
                "delete_account" => {
                    self.delete_account(username, index_supplied);
                    None
                }
                "display_games" => {
                    if args.skip_advertising_updates {
                        None
                    } else {
                        self.clients.get(&index_supplied).map(|tx| {
                            (
                                tx.clone(),
                                true,
                                format!("display_games {:?}", &self.games_light),
                            )
                        })
                    }
                }
                "display_server" => self.display_server(username),
                "draw" => self.draw(index_supplied, command, the_rest.as_slice()),
                "game" => self.game(index_supplied, username, command, the_rest.as_slice()),
                "email" => {
                    self.set_email(index_supplied, username, command, the_rest.first().copied())
                }
                "email_everyone" => {
                    if self.admins.contains(*username) {
                        info!("{index_supplied} {username} email_everyone");
                    } else {
                        error!("{index_supplied} {username} email_everyone");
                        return None;
                    }
                    let emails_bcc = self.bcc_mailboxes(username);
                    let subject = the_rest.first()?;
                    let email_string = the_rest.get(1..)?.join(" ").replace("\\n", "\n");
                    let mut email = lettre::Message::builder();
                    for email_bcc in emails_bcc {
                        email = email.bcc(email_bcc);
                    }
                    let email = email
                        .from("Hnefatafl Org <no-reply@hnefatafl.org>".parse().ok()?)
                        .subject(*subject)
                        .header(ContentType::TEXT_PLAIN)
                        .body(email_string)
                        .ok()?;
                    let credentials =
                        Credentials::new(self.smtp.username.clone(), self.smtp.password.clone());
                    let mailer = SmtpTransport::relay(&self.smtp.service)
                        .ok()?
                        .credentials(credentials)
                        .build();
                    match mailer.send(&email) {
                        Ok(_) => {
                            info!("emails sent successfully!");
                            Some((
                                self.clients.get(&index_supplied)?.clone(),
                                true,
                                (*command).to_string(),
                            ))
                        }
                        Err(err) => {
                            let reply = "could not send emails";
                            error!("{reply}: {err}");
                            Some((
                                self.clients.get(&index_supplied)?.clone(),
                                false,
                                reply.to_string(),
                            ))
                        }
                    }
                }
                "emails_bcc" => {
                    let emails_bcc = self.bcc_send(username);
                    if !emails_bcc.is_empty() {
                        self.clients
                            .get(&index_supplied)?
                            .send(format!("= emails_bcc {emails_bcc}"))
                            .ok()?;
                    }
                    None
                }
                "email_code" => {
                    if let Some(account) = self.accounts.0.get_mut(*username)
                        && let Some(email) = &mut account.email
                        && let (Some(code_1), Some(code_2)) = (email.code, the_rest.first())
                    {
                        if format!("{code_1:x}") == *code_2 {
                            email.verified = true;
                            self.clients
                                .get(&index_supplied)?
                                .send("= email_code".to_string())
                                .ok()?;
                        } else {
                            email.verified = false;
                            self.clients
                                .get(&index_supplied)?
                                .send("? email_code".to_string())
                                .ok()?;
                        }
                        self.save_server();
                    }
                    None
                }
                "email_get" => {
                    if let Some(account) = self.accounts.0.get(*username)
                        && let Some(email) = &account.email
                    {
                        self.clients
                            .get(&index_supplied)?
                            .send(format!("= email {} {}", email.address, email.verified))
                            .ok()?;
                    }
                    None
                }
                "email_reset" => {
                    if let Some(account) = self.accounts.0.get_mut(*username) {
                        account.email = None;
                        self.save_server();
                        Some((
                            self.clients.get(&index_supplied)?.clone(),
                            true,
                            (*command).to_string(),
                        ))
                    } else {
                        None
                    }
                }
                "exit" => {
                    info!("saving active games...");
                    let mut active_games = Vec::new();
                    for game in self.games.0.values() {
                        let mut serialized_game = ServerGameSerialized::from(game);
                        if let Some(game_light) = self.games_light.0.get(&game.id) {
                            serialized_game.timed = game_light.timed.clone();
                        }
                        active_games.push(serialized_game);
                    }
                    let mut file = handle_error(File::create(data_file(ACTIVE_GAMES_FILE)));
                    handle_error(
                        file.write_all(
                            handle_error(postcard::to_allocvec(&active_games)).as_slice(),
                        ),
                    );
                    exit(0);
                }
                "join_game" => self.join_game(
                    username,
                    index_supplied,
                    (*command).to_string(),
                    the_rest.as_slice(),
                ),
                "join_game_pending" => self.join_game_pending(
                    (*username).to_string(),
                    index_supplied,
                    (*command).to_string(),
                    the_rest.as_slice(),
                ),
                "join_tournament" => {
                    if let Some(tournament) = &mut self.tournament {
                        tournament.players.insert(username.to_string());
                        self.save_server();
                        self.tournament_status_all();
                    }
                    None
                }
                "leave_game" => self.leave_game(
                    username,
                    index_supplied,
                    (*command).to_string(),
                    the_rest.as_slice(),
                ),
                "leave_tournament" => {
                    if let Some(tournament) = &mut self.tournament {
                        tournament.players.remove(*username);
                        self.save_server();
                        self.tournament_status_all();
                    }
                    None
                }
                "login" => self.login(
                    username,
                    index_supplied,
                    command,
                    the_rest.as_slice(),
                    option_tx,
                ),
                "logout" => self.logout(username, index_supplied, command),
                "message" => {
                    if Args::parse().skip_message {
                        return None;
                    }
                    let message_file = data_file(MESSAGE_FILE);
                    let mut message = String::new();
                    match fs::read_to_string(&message_file) {
                        Ok(new_message) => message = new_message.trim().replace('\n', "\\n"),
                        Err(err) => match err.kind() {
                            ErrorKind::NotFound => {}
                            _ => error!("Error loading message: {err}"),
                        },
                    }
                    if message.trim().is_empty() {
                        return None;
                    }
                    self.clients
                        .get(&index_supplied)?
                        .send(format!("= message {message}"))
                        .ok()?;
                    None
                }
                "new_game" => self.new_game(username, index_supplied, command, the_rest.as_slice()),
                "ping" => Some((
                    self.clients.get(&index_supplied)?.clone(),
                    true,
                    (*command).to_string(),
                )),
                "reset_password" => {
                    let account = self.accounts.0.get_mut(*username)?;
                    if let Some(email) = &account.email {
                        if email.verified {
                            let day = 60 * 60 * 24;
                            let now = Utc::now().timestamp();
                            if now - account.email_sent > day {
                                let password = format!("{:x}", random::<u32>());
                                account.password = hash_password(&password)?;
                                let message = lettre::Message::builder()
                                .from("Hnefatafl Org <no-reply@hnefatafl.org>".parse().ok()?)
                                .to(email.to_mailbox()?)
                                .subject("Password Reset")
                                .header(ContentType::TEXT_PLAIN)
                                .body(format!(
                                    "Dear {username},\nyour new password is as follows: {password}",
                                ))
                                .ok()?;
                                let credentials = Credentials::new(
                                    self.smtp.username.clone(),
                                    self.smtp.password.clone(),
                                );
                                let mailer = SmtpTransport::relay(&self.smtp.service)
                                    .ok()?
                                    .credentials(credentials)
                                    .build();
                                match mailer.send(&message) {
                                    Ok(_) => {
                                        info!("email sent to {} successfully!", email.address);
                                        account.email_sent = now;
                                        self.save_server();
                                    }
                                    Err(err) => {
                                        error!("could not send email to {}: {err}", email.address);
                                    }
                                }
                            }
                            {
                                error!(
                                    "a password reset email was sent less than a day ago for {username}"
                                );
                            }
                        } else {
                            error!("the email address for account {username} is unverified");
                        }
                    } else {
                        error!("no email exists for account {username}");
                    }
                    None
                }
                "resume_game" => self.resume_game(username, index_supplied, command, &the_rest),
                "request_draw" => self.request_draw(username, index_supplied, command, &the_rest),
                "text" => {
                    let timestamp = timestamp();
                    let the_rest = the_rest.join(" ");
                    info!("{index_supplied} {timestamp} {username} text {the_rest}");
                    let text = format!("= text {timestamp} {username}: {the_rest}");
                    if self.texts.len() >= 32 {
                        self.texts.pop_front();
                    }
                    for tx in &mut self.clients.values() {
                        let _ok = tx.send(text.clone());
                    }
                    self.texts.push_back(text);
                    None
                }
                "texts" => {
                    if !self.texts.is_empty() {
                        let string = Vec::from(self.texts.clone()).join("\n");
                        self.clients.get(&index_supplied)?.send(string).ok()?;
                    }
                    None
                }
                "text_game" => self.text_game(username, index_supplied, command, the_rest),
                "tournament_delete" => {
                    if self.admins.contains(*username) {
                        self.tournament = None;
                        self.save_server();
                        self.tournament_status_all();
                    }
                    None
                }
                "tournament_tree_delete" => {
                    if self.admins.contains(*username)
                        && let Some(tournament) = &mut self.tournament
                    {
                        tournament.tree = None;
                        self.save_server();
                        self.tournament_status_all();
                    }
                    None
                }
                "tournament_date" => {
                    if self.admins.contains(*username) {
                        if let Err(error) = self.tournament_date(&the_rest) {
                            error!("tournament_date: {error}");
                        } else {
                            self.tournament_status_all();
                        }
                    }
                    None
                }
                "tournament_status" => {
                    trace!("tournament_status: {:#?}", self.tournament);
                    if args.skip_advertising_updates {
                        None
                    } else {
                        let tx = self.clients.get(&index_supplied)?;
                        let tournament = ron::ser::to_string(&self.tournament).ok()?;
                        Some((tx.clone(), true, format!("tournament_status {tournament}")))
                    }
                }
                "tournament_start" => {
                    let mut start_tournament = false;
                    if let Some(tournament) = &self.tournament
                        && tournament.tree.is_none()
                        && Utc::now() >= tournament.date
                    {
                        start_tournament = true;
                    }
                    if start_tournament {
                        info!("Starting tournament...");
                        self.tournament_tree();
                        self.save_server();
                        self.tournament_status_all();
                    }
                    None
                }
                "watch_game" => self.watch_game(
                    username,
                    index_supplied,
                    (*command).to_string(),
                    the_rest.as_slice(),
                ),
                "=" => None,
                _ => self.clients.get(&index_supplied).map(|channel| {
                    error!("{index_supplied} {username} {command}");
                    (channel.clone(), false, (*command).to_string())
                }),
            }
        } else {
            error!("{index_username_command:?}");
            None
        }
    }
    fn join_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: String,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        info!("{index_supplied} {username} join_game {id}");
        let Some(game) = self.games_light.0.get_mut(&id) else {
            unreachable!();
        };
        game.challenge_accepted = true;
        let (Some(attacker_tx), Some(defender_tx)) = (game.attacker_channel, game.defender_channel)
        else {
            unreachable!()
        };
        for tx in [&attacker_tx, &defender_tx] {
            self.clients
                .get(tx)?
                .send(format!(
                    "= join_game {} {} {} {:?} {}",
                    game.attacker.clone()?,
                    game.defender.clone()?,
                    game.rated,
                    game.timed,
                    game.board_size,
                ))
                .ok()?;
        }
        let new_game = ServerGame::new(
            Some(self.clients.get(&attacker_tx)?.clone()),
            Some(self.clients.get(&defender_tx)?.clone()),
            game.clone(),
        );
        self.games.0.insert(id, new_game);
        if let Some(account) = self.accounts.0.get_mut(username) {
            account.pending_games.remove(&id);
        }
        self.clients
            .get(&attacker_tx)?
            .send(format!("game {id} generate_move attacker"))
            .ok()?;
        None
    }
    fn join_game_pending(
        &mut self,
        username: String,
        index_supplied: usize,
        mut command: String,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let channel = self.clients.get(&index_supplied)?;
        let Some(id) = the_rest.first() else {
            return Some((channel.clone(), false, command));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((channel.clone(), false, command));
        };
        info!("{index_supplied} {username} join_game_pending {id}");
        let Some(game) = self.games_light.0.get_mut(&id) else {
            command.push_str(" the id doesn't refer to a pending game");
            return Some((channel.clone(), false, command));
        };
        if game.attacker.is_none() {
            game.attacker = Some(username.clone());
            game.attacker_channel = Some(index_supplied);
            if let Some(channel) = game.defender_channel
                && let Some(channel) = self.clients.get(&channel)
            {
                let _ok = channel.send(format!("= challenge_requested {id}"));
            }
        } else if game.defender.is_none() {
            game.defender = Some(username.clone());
            game.defender_channel = Some(index_supplied);
            if let Some(channel) = game.attacker_channel
                && let Some(channel) = self.clients.get(&channel)
            {
                let _ok = channel.send(format!("= challenge_requested {id}"));
            }
        }
        game.challenger.0 = Some(username);
        command.push(' ');
        command.push_str(the_rest.first()?);
        Some((channel.clone(), true, command))
    }
    fn leave_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        mut command: String,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        if let Some(account) = self.accounts.0.get_mut(username) {
            account.pending_games.remove(&id);
        }
        info!("{index_supplied} {username} leave_game {id}");
        let mut remove = false;
        match self.games_light.0.get_mut(&id) {
            Some(game) => {
                if let Some(attacker) = &game.attacker
                    && username == attacker
                {
                    game.attacker = None;
                }
                if let Some(defender) = &game.defender
                    && username == defender
                {
                    game.defender = None;
                }
                if let Some(challenger) = &game.challenger.0
                    && username == challenger
                {
                    game.challenger.0 = None;
                }
                game.spectators.remove(username);
                if game.attacker.is_none() && game.defender.is_none() {
                    remove = true;
                }
            }
            None => return Some((self.clients.get(&index_supplied)?.clone(), false, command)),
        }
        if remove {
            self.games_light.0.remove(&id);
        }
        command.push(' ');
        command.push_str(the_rest.first()?);
        Some((self.clients.get(&index_supplied)?.clone(), true, command))
    }
    fn login(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
        the_rest: &[&str],
        option_tx: Option<Sender<String>>,
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let password_1 = the_rest.join(" ");
        let tx = option_tx?;
        if let Some(account) = self.accounts.0.get_mut(username) {
            // The username is in the database and already logged in.
            if let Some(index_database) = account.logged_in {
                info!("{index_supplied} {username} login failed, {index_database} is logged in");
                Some(((tx), false, (*command).to_string()))
            // The username is in the database, but not logged in yet.
            } else {
                let hash_2 = PasswordHash::try_from(account.password.as_str()).ok()?;
                if let Err(_error) =
                    Argon2::default().verify_password(password_1.as_bytes(), &hash_2)
                {
                    info!("{index_supplied} {username} provided the wrong password");
                    return Some((tx, false, (*command).to_string()));
                }
                info!("{index_supplied} {username} logged in");
                self.clients.insert(index_supplied, tx);
                account.logged_in = Some(index_supplied);
                Some((
                    self.clients.get(&index_supplied)?.clone(),
                    true,
                    (*command).to_string(),
                ))
            }
        // The username is not in the database.
        } else {
            info!("{index_supplied} {username} is not in the database");
            Some((tx, false, (*command).to_string()))
        }
    }
    fn logout(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        // The username is in the database and already logged in.
        if let Some(account) = self.accounts.0.get_mut(username) {
            for id in &account.pending_games {
                if let Some(tx) = &self.tx
                    && let Some(game) = self.games_light.0.get(id)
                    && let TimeSettings::Timed(Time {
                        milliseconds_left, ..
                    }) = game.timed
                    && milliseconds_left < 1_000 * 60 * 60 * 24
                {
                    let _ok =
                        tx.send((format!("{index_supplied} {username} leave_game {id}"), None));
                }
            }
            if let Some(index_database) = account.logged_in
                && index_database == index_supplied
            {
                info!("{index_supplied} {username} logged out");
                account.logged_in = None;
                self.clients
                    .get(&index_supplied)?
                    .send("= logout".to_string())
                    .ok()?;
                self.clients.remove(&index_database);
                return None;
            }
        }
        self.clients
            .get(&index_supplied)
            .map(|sender| (sender.clone(), false, (*command).to_string()))
    }
    /// ```sh
    /// <- new_game attacker rated fischer 900000 10 13
    /// -> = new_game game 6 player-1 _ rated fischer 900000 10 _ false {}
    /// ```
    fn new_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        if the_rest.len() < 6 {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        }
        let role = the_rest.first()?;
        let Ok(role) = Role::from_str(role) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let rated = the_rest.get(1)?;
        let Ok(rated) = Rated::from_str(rated) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let timed = the_rest.get(2)?;
        let minutes = the_rest.get(3)?;
        let add_seconds = the_rest.get(4)?;
        let Ok(timed) = TimeSettings::try_from(vec!["time-settings", timed, minutes, add_seconds])
        else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let board_size = the_rest.get(5)?;
        let board_size = BoardSize::from_str(board_size).ok()?;
        info!(
            "{index_supplied} {username} new_game {} {role} {rated} {timed:?} {board_size}",
            self.game_id
        );
        let game = ServerGameLight::new(
            self.game_id,
            (*username).to_string(),
            rated,
            timed,
            board_size,
            index_supplied,
            role,
        );
        let command = format!("{command} {game:?}");
        self.games_light.0.insert(self.game_id, game);
        if let Some(account) = self.accounts.0.get_mut(username) {
            account.pending_games.insert(self.game_id);
        }
        self.game_id += 1;
        Some((self.clients.get(&index_supplied)?.clone(), true, command))
    }
    #[must_use]
    fn new_tournament_game(&mut self, attacker: &str, defender: &str) -> Id {
        let id = self.game_id;
        self.game_id += 1;
        let game_light = ServerGameLight {
            id,
            attacker: Some(attacker.to_string()),
            defender: Some(defender.to_string()),
            challenger: Challenger(None),
            rated: Rated::Yes,
            timed: TimeEnum::Long.into(),
            attacker_channel: None,
            defender_channel: None,
            spectators: HashMap::new(),
            challenge_accepted: true,
            game_over: false,
            board_size: BoardSize::_11,
        };
        info!(
            "0 server new_tournament_game {id} {} {:?} {}",
            game_light.rated, game_light.timed, game_light.board_size
        );
        let game = ServerGame::new(None, None, game_light.clone());
        self.games_light.0.insert(id, game_light);
        self.games.0.insert(id, game);
        id
    }
    fn resume_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(server_game) = self.games.0.get(&id) else {
            unreachable!()
        };
        let game = &server_game.game;
        let Ok(board) = ron::ser::to_string(game) else {
            unreachable!()
        };
        let texts = &server_game.texts;
        let Ok(texts) = ron::ser::to_string(&texts) else {
            unreachable!()
        };
        info!("{index_supplied} {username} watch_game {id}");
        let Some(game_light) = self.games_light.0.get_mut(&id) else {
            unreachable!();
        };
        if Some((*username).to_string()) == game_light.attacker {
            if let Some(server_game) = self.games.0.get_mut(&id) {
                server_game.attacker_tx =
                    Messenger::new(self.clients.get(&index_supplied)?.clone());
            }
            game_light.attacker_channel = Some(index_supplied);
        } else if Some((*username).to_string()) == game_light.defender {
            if let Some(server_game) = self.games.0.get_mut(&id) {
                server_game.defender_tx =
                    Messenger::new(self.clients.get(&index_supplied)?.clone());
            }
            game_light.defender_channel = Some(index_supplied);
        }
        self.clients
            .get(&index_supplied)?
            .send(format!(
                "= resume_game {} {} {} {:?} {} {board} {texts}",
                game_light.attacker.clone()?,
                game_light.defender.clone()?,
                game_light.rated,
                game_light.timed,
                game_light.board_size,
            ))
            .ok()?;
        None
    }
    fn request_draw(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Some(role) = the_rest.get(1) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Ok(role) = Role::from_str(role) else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        info!("{index_supplied} {username} request_draw {id} {role}");
        let message = format!("request_draw {id} {role}");
        if let Some(game) = self.games.0.get(&id) {
            match role {
                Role::Attacker => {
                    game.defender_tx.send(message);
                }
                Role::Roleless => {}
                Role::Defender => {
                    game.attacker_tx.send(message);
                }
            }
        }
        Some((
            self.clients.get(&index_supplied)?.clone(),
            true,
            (*command).to_string(),
        ))
    }
    fn save_server(&self) {
        if !self.skip_the_data_file {
            let mut server = self.clone();
            for account in server.accounts.0.values_mut() {
                account.logged_in = None;
            }
            match ron::ser::to_string_pretty(&server, ron::ser::PrettyConfig::default()) {
                Ok(string) => {
                    if !string.trim().is_empty() {
                        let users_file = data_file(USERS_FILE);
                        match File::create(&users_file) {
                            Ok(mut file) => {
                                if let Err(error) = file.write_all(string.as_bytes()) {
                                    error!("save file (3): {error}");
                                }
                            }
                            Err(error) => error!("save file (2): {error}"),
                        }
                    }
                }
                Err(error) => error!("save file (1): {error}"),
            }
        }
    }
    fn text_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: &str,
        mut the_rest: Vec<&str>,
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((
                self.clients.get(&index_supplied)?.clone(),
                false,
                (*command).to_string(),
            ));
        };
        let timestamp = timestamp();
        let text = the_rest.split_off(1);
        let mut text = text.join(" ");
        text = format!("{timestamp} {username}: {text}");
        info!("{index_supplied} {username} text_game {id} {text}");
        if let Some(game) = self.games.0.get_mut(&id) {
            game.texts.push_front(text.clone());
        }
        text = format!("= text_game {text}");
        if let Some(game) = self.games_light.0.get(&id) {
            let mut watching = false;
            for (spectator, index) in &game.spectators {
                if spectator == username {
                    watching = true;
                }
                if let Some(sender) = self.clients.get(index) {
                    let _ok = sender.send(text.clone());
                }
            }
            if watching {
                return None;
            }
            if let Some(attacker_channel) = game.attacker_channel
                && let Some(sender) = self.clients.get(&attacker_channel)
            {
                let _ok = sender.send(text.clone());
            }
            if let Some(defender_channel) = game.defender_channel
                && let Some(sender) = self.clients.get(&defender_channel)
            {
                let _ok = sender.send(text.clone());
            }
        }
        None
    }
    fn tournament_date(&mut self, the_rest: &[&str]) -> anyhow::Result<()> {
        let mut tournament = Tournament::default();
        let Some(date) = the_rest.first() else {
            return Err(anyhow::Error::msg("tournament_date: date is empty"));
        };
        let datetime = match DateTime::parse_from_str(
            &format!("{date} 00:00:00 +0000"),
            "%Y-%m-%d %H:%M:%S %z",
        ) {
            Ok(datetime) => datetime,
            Err(error) => return Err(anyhow::Error::msg(format!("tournament_date: {error}"))),
        };
        tournament.date = datetime.to_utc();
        self.tournament = Some(tournament);
        self.save_server();
        Ok(())
    }
    fn tournament_ready_to_playing(&mut self) {
        let mut new_games = Vec::new();
        if let Some(tournament) = &mut self.tournament
            && let Some(tree) = &mut tournament.tree
        {
            for (i, round) in tree.rounds.iter_mut().enumerate() {
                for (j, statuses) in round.chunks_mut(2).enumerate() {
                    let (status_1, status_2) = statuses.split_at_mut(1);
                    let (Some(status_1), Some(status_2)) =
                        (status_1.first_mut(), status_2.first_mut())
                    else {
                        continue;
                    };
                    if let tournament::Status::Ready(player_1) = status_1.clone()
                        && let tournament::Status::Ready(player_2) = status_2.clone()
                    {
                        *status_1 = tournament::Status::Playing(player_1.clone());
                        *status_2 = tournament::Status::Playing(player_2.clone());
                        new_games.push((player_1.name, player_2.name, i, j));
                    }
                }
            }
        }
        trace!("new_games: {new_games:#?}");
        for (player_1, player_2, round, chunk) in new_games {
            let id_1 = self.new_tournament_game(&player_1, &player_2);
            let id_2 = self.new_tournament_game(&player_2, &player_1);
            let game_players = Players {
                round,
                chunk,
                player_1: Wins {
                    name: player_1,
                    attacker: 0,
                    defender: 0,
                },
                player_2: Wins {
                    name: player_2,
                    attacker: 0,
                    defender: 0,
                },
            };
            let game_players = Arc::new(Mutex::new(game_players));
            if let Some(tournament) = &mut self.tournament
                && let Some(tree) = &mut tournament.tree
            {
                tree.active_games.insert(id_1, game_players.clone());
                tree.active_games.insert(id_2, game_players.clone());
            }
        }
    }
    fn tournament_status_all(&self) {
        trace!("tournament_status: {:#?}", self.tournament);
        if let Ok(mut tournament) = ron::ser::to_string(&self.tournament) {
            tournament = format!("= tournament_status {tournament}");
            for tx in self.clients.values() {
                let _ok = tx.send(tournament.clone());
            }
        }
    }
    fn tournament_tree(&mut self) {
        let Some(tournament) = &mut self.tournament else {
            return;
        };
        let mut players = Vec::new();
        for player in &tournament.players {
            if let Some(account) = self.accounts.0.get(player) {
                players.push(Player {
                    name: player.clone(),
                    rating: account.rating.rating.round_ties_even(),
                });
            }
        }
        let mut rng = thread_rng();
        players.shuffle(&mut rng);
        players.sort_unstable_by(|a, b| a.rating.total_cmp(&b.rating));
        tournament.tree = Some(TournamentTree {
            active_games: HashMap::new(),
            rounds: vec![generate_round_one(players)],
        });
        self.tournament_update_wins();
        self.tournament_ready_to_playing();
    }
    fn tournament_update_wins(&mut self) {
        if let Some(tournament) = &mut self.tournament
            && let Some(tree) = &mut tournament.tree
        {
            let mut updates = Vec::new();
            for (i, round) in tree.rounds.iter_mut().enumerate() {
                let mut player = None;
                let mut move_forward = false;
                let new_round_length = round.len() / 2;
                for (mut j, status) in round.iter_mut().enumerate() {
                    if j % 2 == 0 {
                        move_forward = false;
                        player = None;
                    }
                    j /= 2;
                    if j % 2 != 0 {
                        j = new_round_length - j;
                    }
                    match &status {
                        tournament::Status::Lost(_)
                        | tournament::Status::Playing(_)
                        | tournament::Status::Waiting => continue,
                        tournament::Status::None => {
                            move_forward = true;
                        }
                        tournament::Status::Ready(p) => player = Some(p.clone()),
                        tournament::Status::Won(player) => {
                            updates.push((i + 1, j, player.clone()));
                        }
                    }
                    if move_forward && let Some(player) = &player {
                        updates.push((i + 1, j, player.clone()));
                    }
                }
            }
            for (i, j, player) in updates {
                let len;
                if let Some(round) = tree.rounds.get(i - 1) {
                    len = round.len() / 2;
                } else {
                    error!("tree.rounds.get({i} - 1) is None");
                    return;
                }
                if tree.rounds.get(i).is_none() {
                    let mut round = Vec::new();
                    for _ in 0..len {
                        round.push(tournament::Status::Waiting);
                    }
                    tree.rounds.push(round);
                }
                if let Some(round) = tree.rounds.get_mut(i)
                    && let Some(status) = round.get_mut(j)
                {
                    match status {
                        tournament::Status::Lost(_)
                        | tournament::Status::None
                        | tournament::Status::Ready(_)
                        | tournament::Status::Playing(_)
                        | tournament::Status::Won(_) => {}
                        tournament::Status::Waiting => *status = tournament::Status::Ready(player),
                    }
                } else {
                    error!("tree.rounds[{i}][{j}] is None");
                    return;
                }
            }
        }
    }
    fn watch_game(
        &mut self,
        username: &str,
        index_supplied: usize,
        command: String,
        the_rest: &[&str],
    ) -> Option<(mpsc::Sender<String>, bool, String)> {
        let Some(id) = the_rest.first() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        let Ok(id) = id.parse::<Id>() else {
            return Some((self.clients.get(&index_supplied)?.clone(), false, command));
        };
        if let Some(game) = self.games_light.0.get_mut(&id) {
            game.spectators.insert(username.to_string(), index_supplied);
        }
        let Some(server_game) = self.games.0.get(&id) else {
            unreachable!()
        };
        let game = &server_game.game;
        let Ok(board) = ron::ser::to_string(game) else {
            unreachable!()
        };
        let texts = &server_game.texts;
        let Ok(texts) = ron::ser::to_string(&texts) else {
            unreachable!()
        };
        info!("{index_supplied} {username} watch_game {id}");
        let Some(game) = self.games_light.0.get_mut(&id) else {
            unreachable!()
        };
        self.clients
            .get(&index_supplied)?
            .send(format!(
                "= watch_game {} {} {} {:?} {} {board} {texts}",
                game.attacker.clone()?,
                game.defender.clone()?,
                game.rated,
                game.timed,
                game.board_size,
            ))
            .ok()?;
        None
    }
}