~starkingdoms/starkingdoms

ref: ca28644b9fae1cd995105d477077e9cd8268c675 starkingdoms/server/src/main.rs -rw-r--r-- 5.5 KiB
ca28644b — ghostlyzsh arrow thingy no more line 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// StarKingdoms.IO, an open source browser game
//      Copyright (C) 2023 ghostly_zsh (and contributors, depending on the license you choose)
//
//      <license disclaimer here>

#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::missing_panics_doc)]

use crate::manager::{ClientManager};
use crate::timer::timer_main;
use crate::entity::Entity;
use async_std::net::{TcpListener};
use async_std::sync::Arc;
use async_std::sync::RwLock;
use entity::EntityHandler;
use lazy_static::lazy_static;
use log::{error, info, warn, Level};
use manager::PhysicsData;
use nalgebra::vector;
use parking_lot::deadlock::check_deadlock;
use rapier2d_f64::prelude::{
    BroadPhase, CCDSolver, ColliderSet, ImpulseJointSet, IntegrationParameters, IslandManager,
    MultibodyJointSet, NarrowPhase, RigidBodySet,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::thread;
use std::time::Duration;
use crate::tcp_handler::handle_request;

pub mod handler;
pub mod manager;
pub mod timer;
#[macro_use]
pub mod macros;
pub mod api;
pub mod entity;
pub mod module;
pub mod orbit;
pub mod planet;
pub mod tcp_handler;

const SCALE: f64 = 10.0;

lazy_static! {
    static ref CMGR: ClientManager = ClientManager {
        handlers: Arc::new(RwLock::new(HashMap::default())),
        usernames: Arc::new(RwLock::new(HashMap::default())),
    };
    static ref DATA: Arc<RwLock<PhysicsData>> = Arc::new(RwLock::new(PhysicsData {
        gravity: vector![0.0, 0.0],
        integration_parameters: IntegrationParameters {
            dt: 1.0 / 20.0,
            joint_erp: 0.2,
            erp: 0.5,
            max_stabilization_iterations: 16,
            ..Default::default()
        },
        island_manager: IslandManager::new(),
        broad_phase: BroadPhase::new(),
        narrow_phase: NarrowPhase::new(),
        rigid_body_set: RigidBodySet::new(),
        collider_set: ColliderSet::new(),
        impulse_joint_set: ImpulseJointSet::new(),
        multibody_joint_set: MultibodyJointSet::new(),
        ccd_solver: CCDSolver::new(),
    }));
    static ref ENTITIES: Arc<RwLock<EntityHandler>> = Arc::new(RwLock::new(EntityHandler::new()));
}

pub const PANIC_ON_DEADLOCK: bool = true;

//noinspection ALL
#[async_std::main]
async fn main() {
    #[allow(clippy::expect_used)]
    {
        simple_logger::init_with_level(Level::Debug).expect("Unable to start logging service");
    }

    info!(
        "StarKingdoms server (v: {}, build {}) - initializing",
        env!("STK_VERSION"),
        env!("STK_BUILD")
    );

    info!("{}", std::mem::size_of::<Entity>());

    if std::env::var("STK_API_KEY").is_err() {
        error!(
            "Unable to read the API key from STK_API_KEY. Ensure it is set, and has a valid value."
        );
        std::process::exit(1);
    }
    if std::env::var("STK_API_URL").is_err() {
        error!("Unable to read the API server URL from STK_API_URL. Ensure it is set, and has a valid value.");
        std::process::exit(1);
    }

    info!("Starting deadlock detector...");

    thread::spawn(move || loop {
        thread::sleep(Duration::from_secs(10));
        let deadlocks = check_deadlock();
        if deadlocks.is_empty() {
            continue;
        }

        error!("---- DEADLOCK DETECTED ----");
        error!("{} deadlocks were detected.", deadlocks.len());
        for (i, threads) in deadlocks.iter().enumerate() {
            error!("-= Deadlock #{}", i);
            for t in threads {
                error!("-= Thread ID = {:#?}", t.thread_id());
                error!("-= Backtrace:\n{:#?}", t.backtrace());
            }
        }
        if PANIC_ON_DEADLOCK {
            error!("StarKingdoms is configured to panic when deadlocks are detected.");
            error!("Bye!");
            panic!("Deadlock detected on one or more threads");
        } else {
            error!("StarKingdoms is not configured to panic when deadlocks are detected.");
        }
    });

    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));

    info!("Listening on {} for HTTP/WebSocket connections", addr);

    let mgr_timer = CMGR.clone();
    let physics_data = DATA.clone();
    let entities_timer = ENTITIES.clone();
    let _timer_thread = async_std::task::spawn(async move {
        match timer_main(mgr_timer, physics_data, entities_timer).await {
            Ok(_) => (),
            Err(e) => {
                error!("timer thread exited with error: {}", e);
                std::process::exit(1);
            }
        }
    });

    let try_socket = TcpListener::bind(&addr).await;

    let listener = match try_socket {
        Ok(l) => l,
        Err(e) => {
            error!("error binding to socket: {}", e);
            std::process::exit(1);
        }
    };

    while let Ok((stream, peer_addr)) = listener.accept().await {
        async_std::task::spawn(handle_request(
            stream,
            peer_addr,
            CMGR.clone(),
            ENTITIES.clone(),
            DATA.clone(),
        ));
    }
}

#[derive(Serialize, Deserialize)]
pub struct ServerPingResponse {
    pub version: ServerPingResponseVersion,
    pub players: u32,
    pub description: String,
}

#[derive(Serialize, Deserialize)]
pub struct ServerPingResponseVersion {
    pub name: String,
    pub number: String,
    pub protocol: u32,
    pub channel: String,
    pub build: String,
}