use crate::app::App;
use crate::error::Result;
use crate::middleware::authorization::CurrentPlayer;
use crate::response::from_err;
use crate::{VERSION, res};
use axum::extract::{Extension, Json, State};
use axum::response::Response;
use nil_core::world::config::WorldId;
use nil_payload::request::world::*;
use nil_payload::response::world::*;
use nil_server_types::world::RemoteWorld;
use semver::Version;
use std::cmp::Reverse;
use std::sync::Arc;
pub async fn create(
State(app): State<App>,
Extension(player): Extension<CurrentPlayer>,
Json(req): Json<CreateRemoteWorldRequest>,
) -> Response {
if app.server_kind().is_remote() {
let Ok(version) = Version::parse(VERSION) else {
return res!(INTERNAL_SERVER_ERROR);
};
app
.create_remote(&req.options)
.player_id(player)
.maybe_world_description(req.description)
.maybe_world_password(req.password)
.maybe_round_duration(req.round_duration)
.server_version(version)
.call()
.await
.map(|world_id| res!(CREATED, CreateRemoteWorldResponse(world_id)))
.unwrap_or_else(from_err)
} else {
res!(FORBIDDEN)
}
}
pub async fn delete(
State(app): State<App>,
Extension(player): Extension<CurrentPlayer>,
Json(req): Json<DeleteRemoteWorldRequest>,
) -> Response {
if app.server_kind().is_remote() {
let result = try {
let database = app.database();
if database
.was_game_created_by(req.world, player.0)
.await?
{
database.delete_game(req.world).await?;
drop(app.remove(req.world));
} else {
return res!(FORBIDDEN);
}
};
result
.map(|()| res!(NO_CONTENT))
.unwrap_or_else(from_err)
} else {
res!(FORBIDDEN)
}
}
pub async fn get(State(app): State<App>, Json(req): Json<GetRemoteWorldRequest>) -> Response {
if app.server_kind().is_remote() {
make_remote_world(&app, req.world)
.await
.map(|world| res!(OK, GetRemoteWorldResponse(world)))
.unwrap_or_else(from_err)
} else {
res!(FORBIDDEN)
}
}
pub async fn get_all(State(app): State<App>) -> Response {
if app.server_kind().is_remote() {
let ids = app.world_ids();
let mut worlds = Vec::with_capacity(ids.len());
for id in ids {
if let Ok(world) = make_remote_world(&app, id).await {
worlds.push(world);
}
}
worlds.sort_by_key(|b| Reverse(b.config.id()));
res!(OK, GetRemoteWorldsResponse(worlds))
} else {
res!(FORBIDDEN)
}
}
async fn make_remote_world(app: &App, id: WorldId) -> Result<RemoteWorld> {
let database = app.database();
let game = database.get_game(id).await?;
let user = database
.get_user_by_id(game.created_by)
.await?;
let world = app.get(id)?;
let world = world.read().await;
let mut active_players: u32 = 0;
let mut total_players: u32 = 0;
for player in world.players() {
if player.is_active() {
active_players = active_players.saturating_add(1);
}
total_players = total_players.saturating_add(1);
}
Ok(RemoteWorld {
config: Arc::unwrap_or_clone(world.config()),
description: game.description,
created_by: user.player_id.into(),
created_at: game.created_at.into(),
updated_at: game.updated_at.into(),
has_password: game.password.is_some(),
current_round: world.round().id(),
round_duration: game.round_duration.map(Into::into),
active_players,
total_players,
continent_size: world.continent().size(),
})
}
pub async fn get_limit(State(app): State<App>) -> Response {
res!(OK, GetRemoteWorldLimitResponse(app.world_limit()))
}
pub async fn get_limit_per_user(State(app): State<App>) -> Response {
res!(
OK,
GetRemoteWorldLimitPerUserResponse(app.world_limit_per_user())
)
}