From f25cffbe986a0fd5687618ed794730bfd78cab9a Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Thu, 4 Jul 2024 11:41:04 -0700 Subject: [PATCH] fix(group): require > 1 use count on invites --- fern/definition/servers/common.yml | 2 +- fern/definition/servers/servers.yml | 12 + lib/convert/src/impls/ds.rs | 253 ++++-------- proto/backend/ds.proto | 304 ++++++++++++++ proto/backend/dynamic_servers.proto | 313 -------------- sdks/full/go/servers/servers.go | 29 ++ sdks/full/go/servers/servers/client.go | 80 ++++ sdks/full/go/servers/types.go | 8 +- sdks/full/openapi/openapi.yml | 94 ++++- sdks/full/openapi_compat/openapi.yml | 68 ++- sdks/full/rust-cli/.openapi-generator/FILES | 2 + sdks/full/rust-cli/README.md | 2 + .../rust-cli/docs/ServersGetServerResponse.md | 11 + sdks/full/rust-cli/docs/ServersPort.md | 2 +- sdks/full/rust-cli/docs/ServersServersApi.md | 31 ++ .../rust-cli/src/apis/servers_servers_api.rs | 44 ++ sdks/full/rust-cli/src/models/mod.rs | 2 + .../src/models/servers_get_server_response.rs | 28 ++ sdks/full/rust-cli/src/models/servers_port.rs | 6 +- sdks/full/rust/.openapi-generator/FILES | 2 + sdks/full/rust/README.md | 2 + .../rust/docs/ServersGetServerResponse.md | 11 + sdks/full/rust/docs/ServersPort.md | 2 +- sdks/full/rust/docs/ServersServersApi.md | 31 ++ .../full/rust/src/apis/servers_servers_api.rs | 44 ++ sdks/full/rust/src/models/mod.rs | 2 + .../src/models/servers_get_server_response.rs | 28 ++ sdks/full/rust/src/models/servers_port.rs | 6 +- .../resources/servers/client/Client.ts | 123 +++++- svc/Cargo.lock | 39 ++ svc/Cargo.toml | 1 + svc/api/group/src/route/groups.rs | 4 +- svc/api/servers/Cargo.toml | 1 + svc/api/servers/src/route/mod.rs | 4 + svc/api/servers/src/route/servers.rs | 71 +++- svc/api/traefik-provider/Cargo.toml | 1 + .../src/route/game_guard/dynamic_servers.rs | 31 +- .../src/route/game_guard/mod.rs | 2 +- .../migrations/20240501133910_init.up.sql | 6 +- svc/pkg/ds/ops/server-create/Cargo.toml | 1 + svc/pkg/ds/ops/server-create/src/lib.rs | 308 +++++++------- svc/pkg/ds/ops/server-create/src/nomad_job.rs | 14 +- .../ops/server-create/src/util_mm/consts.rs | 39 -- .../ops/server-create/src/util_mm/defaults.rs | 4 - .../ds/ops/server-create/src/util_mm/key.rs | 207 ---------- .../ds/ops/server-create/src/util_mm/mod.rs | 48 --- .../ds/ops/server-create/src/util_mm/test.rs | 1 - .../server-create/src/util_mm/verification.rs | 391 ------------------ .../src/util_mm/version_migrations.rs | 8 - .../ds/ops/server-create/tests/integration.rs | 68 ++- .../server-create/tests/print_test_data.rs | 12 +- svc/pkg/ds/ops/server-delete/src/lib.rs | 137 +++--- svc/pkg/ds/ops/server-get/Cargo.toml | 26 ++ svc/pkg/ds/ops/server-get/Service.toml | 7 + svc/pkg/ds/ops/server-get/src/lib.rs | 240 +++++++++++ .../ds/ops/server-get/tests/integration.rs | 79 ++++ svc/pkg/ds/types/server-create.proto | 26 +- svc/pkg/ds/types/server-get.proto | 14 + svc/pkg/ds/util/Cargo.toml | 2 +- svc/pkg/ds/util/src/defaults.rs | 4 - svc/pkg/ds/util/src/key.rs | 209 ---------- svc/pkg/ds/util/src/lib.rs | 47 +-- svc/pkg/ds/util/src/verification.rs | 391 ------------------ svc/pkg/ds/util/src/version_migrations.rs | 8 - 64 files changed, 1802 insertions(+), 2191 deletions(-) create mode 100644 proto/backend/ds.proto delete mode 100644 proto/backend/dynamic_servers.proto create mode 100644 sdks/full/rust-cli/docs/ServersGetServerResponse.md create mode 100644 sdks/full/rust-cli/src/models/servers_get_server_response.rs create mode 100644 sdks/full/rust/docs/ServersGetServerResponse.md create mode 100644 sdks/full/rust/src/models/servers_get_server_response.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/consts.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/defaults.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/key.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/mod.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/test.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/verification.rs delete mode 100644 svc/pkg/ds/ops/server-create/src/util_mm/version_migrations.rs create mode 100644 svc/pkg/ds/ops/server-get/Cargo.toml create mode 100644 svc/pkg/ds/ops/server-get/Service.toml create mode 100644 svc/pkg/ds/ops/server-get/src/lib.rs create mode 100644 svc/pkg/ds/ops/server-get/tests/integration.rs create mode 100644 svc/pkg/ds/types/server-get.proto delete mode 100644 svc/pkg/ds/util/src/defaults.rs delete mode 100644 svc/pkg/ds/util/src/key.rs delete mode 100644 svc/pkg/ds/util/src/verification.rs delete mode 100644 svc/pkg/ds/util/src/version_migrations.rs diff --git a/fern/definition/servers/common.yml b/fern/definition/servers/common.yml index 6fa2a4bbf..4919a2d53 100644 --- a/fern/definition/servers/common.yml +++ b/fern/definition/servers/common.yml @@ -48,7 +48,7 @@ types: properties: routing: PortRouting server_port: optional - public_host: optional + public_hostname: optional public_port: optional PortRouting: diff --git a/fern/definition/servers/servers.yml b/fern/definition/servers/servers.yml index 5597b3d53..054da127e 100644 --- a/fern/definition/servers/servers.yml +++ b/fern/definition/servers/servers.yml @@ -7,6 +7,15 @@ service: auth: true base-path: /servers/servers endpoints: + get: + path: "/{server_id}" + method: GET + docs: Gets a dynamic server. + path-parameters: + server_id: + docs: The id of the server to destroy + type: uuid + response: GetServerResponse create: path: "" method: POST @@ -34,6 +43,9 @@ service: response: DestroyServerResponse types: + GetServerResponse: + properties: + servers: list CreateServerRequest: properties: datacenter: diff --git a/lib/convert/src/impls/ds.rs b/lib/convert/src/impls/ds.rs index b7d040049..b571a34bb 100644 --- a/lib/convert/src/impls/ds.rs +++ b/lib/convert/src/impls/ds.rs @@ -7,13 +7,9 @@ use rivet_operation::prelude::*; use crate::{ApiFrom, ApiInto, ApiTryFrom, ApiTryInto}; use serde_json::to_value; -impl ApiTryFrom for models::ServersServer { +impl ApiTryFrom for models::ServersServer { type Error = GlobalError; - fn api_try_from( - value: backend::dynamic_servers::Server, - ) -> GlobalResult { - let backend::dynamic_servers::server::Runtime::DockerRuntime(docker_runtime) = - unwrap!(value.runtime); + fn api_try_from(value: backend::ds::Server) -> GlobalResult { Ok(models::ServersServer { cluster_id: unwrap!(value.cluster_id).as_uuid(), create_ts: value.create_ts, @@ -24,25 +20,34 @@ impl ApiTryFrom for models::ServersServer { metadata: Some(to_value(value.metadata).unwrap()), resources: Box::new(unwrap!(value.resources).api_into()), server_id: unwrap!(value.server_id).as_uuid(), - args: Some(docker_runtime.args), - environment: Some(docker_runtime.environment), - image_id: unwrap!(docker_runtime.image_id).as_uuid(), - network: Box::new(unwrap!(docker_runtime.network).api_try_into()?), + args: Some(value.args), + environment: Some(value.environment), + image_id: unwrap!(value.image_id).as_uuid(), + network: Box::new(models::ServersNetwork { + mode: Some( + unwrap!(backend::ds::NetworkMode::from_i32(value.network_mode)).api_into(), + ), + ports: value + .network_ports + .into_iter() + .map(|(s, p)| Ok((s, p.api_try_into()?))) + .collect::>>()?, + }), }) } } -impl ApiFrom for backend::dynamic_servers::ServerResources { - fn api_from(value: models::ServersResources) -> backend::dynamic_servers::ServerResources { - backend::dynamic_servers::ServerResources { +impl ApiFrom for backend::ds::ServerResources { + fn api_from(value: models::ServersResources) -> backend::ds::ServerResources { + backend::ds::ServerResources { cpu_millicores: value.cpu, memory_mib: value.memory, } } } -impl ApiFrom for models::ServersResources { - fn api_from(value: backend::dynamic_servers::ServerResources) -> models::ServersResources { +impl ApiFrom for models::ServersResources { + fn api_from(value: backend::ds::ServerResources) -> models::ServersResources { models::ServersResources { cpu: value.cpu_millicores, memory: value.memory_mib, @@ -50,238 +55,150 @@ impl ApiFrom for models::ServersResou } } -impl ApiTryInto for backend::dynamic_servers::DockerNetwork { - type Error = GlobalError; - - fn api_try_into(self) -> GlobalResult { - Ok(models::ServersNetwork { - mode: Some( - unwrap!(backend::dynamic_servers::DockerNetworkMode::from_i32( - self.mode - )) - .api_into(), - ), - ports: self - .ports - .into_iter() - .map(|(s, p)| Ok((s, p.api_try_into()?))) - .collect::>>()?, - }) - } -} - -impl ApiFrom for backend::dynamic_servers::DockerNetworkMode { - fn api_from(value: models::ServersNetworkMode) -> backend::dynamic_servers::DockerNetworkMode { +impl ApiFrom for backend::ds::NetworkMode { + fn api_from(value: models::ServersNetworkMode) -> backend::ds::NetworkMode { match value { - models::ServersNetworkMode::Bridge => { - backend::dynamic_servers::DockerNetworkMode::Bridge - } - models::ServersNetworkMode::Host => backend::dynamic_servers::DockerNetworkMode::Host, + models::ServersNetworkMode::Bridge => backend::ds::NetworkMode::Bridge, + models::ServersNetworkMode::Host => backend::ds::NetworkMode::Host, } } } -impl ApiFrom for models::ServersNetworkMode { - fn api_from(value: backend::dynamic_servers::DockerNetworkMode) -> models::ServersNetworkMode { +impl ApiFrom for models::ServersNetworkMode { + fn api_from(value: backend::ds::NetworkMode) -> models::ServersNetworkMode { match value { - backend::dynamic_servers::DockerNetworkMode::Bridge => { - models::ServersNetworkMode::Bridge - } - backend::dynamic_servers::DockerNetworkMode::Host => models::ServersNetworkMode::Host, + backend::ds::NetworkMode::Bridge => models::ServersNetworkMode::Bridge, + backend::ds::NetworkMode::Host => models::ServersNetworkMode::Host, } } } -impl ApiTryFrom for backend::dynamic_servers::DockerPort { - type Error = GlobalError; - - fn api_try_from( - value: models::ServersPort, - ) -> GlobalResult { - Ok(backend::dynamic_servers::DockerPort { - port: value.server_port, - routing: Some((*value.routing).api_try_into()?), - }) - } -} - -impl ApiTryFrom for models::ServersPort { +impl ApiTryFrom for models::ServersPort { type Error = GlobalError; - fn api_try_from( - value: backend::dynamic_servers::DockerPort, - ) -> GlobalResult { + fn api_try_from(value: backend::ds::Port) -> GlobalResult { Ok(models::ServersPort { - server_port: value.port, + server_port: value.server_port, routing: Box::new(unwrap!(value.routing).api_try_into()?), - public_host: None, - public_port: None, + public_hostname: value.public_hostname, + public_port: value.public_port, }) } } -impl ApiTryFrom for backend::dynamic_servers::docker_port::Routing { +impl ApiTryFrom for backend::ds::port::Routing { type Error = GlobalError; - fn api_try_from( - value: models::ServersPortRouting, - ) -> GlobalResult { + fn api_try_from(value: models::ServersPortRouting) -> GlobalResult { match (value.game_guard, value.host) { - (Some(game_guard), None) => Ok( - backend::dynamic_servers::docker_port::Routing::GameGuard((*game_guard).api_into()), - ), - (None, Some(host)) => Ok(backend::dynamic_servers::docker_port::Routing::Host( - (*host).api_into(), + (Some(game_guard), None) => Ok(backend::ds::port::Routing::GameGuard( + (*game_guard).api_into(), )), + (None, Some(host)) => Ok(backend::ds::port::Routing::Host((*host).api_into())), (None, None) => bail_with!(SERVERS_NO_PORT_ROUTERS), _ => bail_with!(SERVERS_MULTIPLE_PORT_ROUTERS), } } } -impl ApiTryFrom for models::ServersPortRouting { +impl ApiTryFrom for models::ServersPortRouting { type Error = GlobalError; - fn api_try_from( - value: backend::dynamic_servers::docker_port::Routing, - ) -> GlobalResult { + fn api_try_from(value: backend::ds::port::Routing) -> GlobalResult { match value { - backend::dynamic_servers::docker_port::Routing::GameGuard(game_guard) => { - Ok(models::ServersPortRouting { - game_guard: Some(Box::new(game_guard.api_try_into()?)), - host: None, - }) - } - backend::dynamic_servers::docker_port::Routing::Host(host) => { - Ok(models::ServersPortRouting { - game_guard: None, - host: Some(Box::new(host.api_try_into()?)), - }) - } + backend::ds::port::Routing::GameGuard(game_guard) => Ok(models::ServersPortRouting { + game_guard: Some(Box::new(game_guard.api_try_into()?)), + host: None, + }), + backend::ds::port::Routing::Host(host) => Ok(models::ServersPortRouting { + game_guard: None, + host: Some(Box::new(host.api_try_into()?)), + }), } } } -impl ApiFrom for backend::dynamic_servers::DockerGameGuardRouting { - fn api_from( - value: models::ServersGameGuardRouting, - ) -> backend::dynamic_servers::DockerGameGuardRouting { - backend::dynamic_servers::DockerGameGuardRouting { - protocol: backend::dynamic_servers::GameGuardProtocol::api_from( +impl ApiFrom for backend::ds::GameGuardRouting { + fn api_from(value: models::ServersGameGuardRouting) -> backend::ds::GameGuardRouting { + backend::ds::GameGuardRouting { + protocol: backend::ds::GameGuardProtocol::api_from( value.protocol.unwrap_or_default().into(), ) as i32, } } } -impl ApiTryFrom - for models::ServersGameGuardRouting -{ +impl ApiTryFrom for models::ServersGameGuardRouting { type Error = GlobalError; fn api_try_from( - value: backend::dynamic_servers::DockerGameGuardRouting, + value: backend::ds::GameGuardRouting, ) -> GlobalResult { Ok(models::ServersGameGuardRouting { protocol: Some( - unwrap!(backend::dynamic_servers::GameGuardProtocol::from_i32( - value.protocol - )) - .api_into(), + unwrap!(backend::ds::GameGuardProtocol::from_i32(value.protocol)).api_into(), ), }) } } -impl ApiFrom for backend::dynamic_servers::DockerHostRouting { - fn api_from(value: models::ServersHostRouting) -> backend::dynamic_servers::DockerHostRouting { - backend::dynamic_servers::DockerHostRouting { - protocol: backend::dynamic_servers::HostProtocol::api_from( - value.protocol.unwrap_or_default().into(), - ) as i32, +impl ApiFrom for backend::ds::HostRouting { + fn api_from(value: models::ServersHostRouting) -> backend::ds::HostRouting { + backend::ds::HostRouting { + protocol: backend::ds::HostProtocol::api_from(value.protocol.unwrap_or_default().into()) + as i32, } } } -impl ApiTryFrom for models::ServersHostRouting { +impl ApiTryFrom for models::ServersHostRouting { type Error = GlobalError; - fn api_try_from( - value: backend::dynamic_servers::DockerHostRouting, - ) -> GlobalResult { + fn api_try_from(value: backend::ds::HostRouting) -> GlobalResult { Ok(models::ServersHostRouting { - protocol: Some( - unwrap!(backend::dynamic_servers::HostProtocol::from_i32( - value.protocol - )) - .api_into(), - ), + protocol: Some(unwrap!(backend::ds::HostProtocol::from_i32(value.protocol)).api_into()), }) } } -impl ApiFrom for backend::dynamic_servers::GameGuardProtocol { - fn api_from( - value: models::ServersGameGuardProtocol, - ) -> backend::dynamic_servers::GameGuardProtocol { +impl ApiFrom for backend::ds::GameGuardProtocol { + fn api_from(value: models::ServersGameGuardProtocol) -> backend::ds::GameGuardProtocol { match value { - models::ServersGameGuardProtocol::Udp => { - backend::dynamic_servers::GameGuardProtocol::Udp - } - models::ServersGameGuardProtocol::Tcp => { - backend::dynamic_servers::GameGuardProtocol::Tcp - } - models::ServersGameGuardProtocol::Http => { - backend::dynamic_servers::GameGuardProtocol::Http - } - models::ServersGameGuardProtocol::Https => { - backend::dynamic_servers::GameGuardProtocol::Https - } - models::ServersGameGuardProtocol::TcpTls => { - backend::dynamic_servers::GameGuardProtocol::TcpTls - } + models::ServersGameGuardProtocol::Udp => backend::ds::GameGuardProtocol::Udp, + models::ServersGameGuardProtocol::Tcp => backend::ds::GameGuardProtocol::Tcp, + models::ServersGameGuardProtocol::Http => backend::ds::GameGuardProtocol::Http, + models::ServersGameGuardProtocol::Https => backend::ds::GameGuardProtocol::Https, + models::ServersGameGuardProtocol::TcpTls => backend::ds::GameGuardProtocol::TcpTls, } } } -impl ApiFrom for models::ServersGameGuardProtocol { - fn api_from( - value: backend::dynamic_servers::GameGuardProtocol, - ) -> models::ServersGameGuardProtocol { +impl ApiFrom for models::ServersGameGuardProtocol { + fn api_from(value: backend::ds::GameGuardProtocol) -> models::ServersGameGuardProtocol { match value { - backend::dynamic_servers::GameGuardProtocol::Udp => { - models::ServersGameGuardProtocol::Udp - } - backend::dynamic_servers::GameGuardProtocol::Tcp => { - models::ServersGameGuardProtocol::Tcp - } - backend::dynamic_servers::GameGuardProtocol::Http => { - models::ServersGameGuardProtocol::Http - } - backend::dynamic_servers::GameGuardProtocol::Https => { - models::ServersGameGuardProtocol::Https - } - backend::dynamic_servers::GameGuardProtocol::TcpTls => { - models::ServersGameGuardProtocol::TcpTls - } + backend::ds::GameGuardProtocol::Udp => models::ServersGameGuardProtocol::Udp, + backend::ds::GameGuardProtocol::Tcp => models::ServersGameGuardProtocol::Tcp, + backend::ds::GameGuardProtocol::Http => models::ServersGameGuardProtocol::Http, + backend::ds::GameGuardProtocol::Https => models::ServersGameGuardProtocol::Https, + backend::ds::GameGuardProtocol::TcpTls => models::ServersGameGuardProtocol::TcpTls, } } } -impl ApiFrom for backend::dynamic_servers::HostProtocol { - fn api_from(value: models::ServersHostProtocol) -> backend::dynamic_servers::HostProtocol { +impl ApiFrom for backend::ds::HostProtocol { + fn api_from(value: models::ServersHostProtocol) -> backend::ds::HostProtocol { match value { - models::ServersHostProtocol::Udp => backend::dynamic_servers::HostProtocol::HostUdp, - models::ServersHostProtocol::Tcp => backend::dynamic_servers::HostProtocol::HostTcp, + models::ServersHostProtocol::Udp => backend::ds::HostProtocol::HostUdp, + models::ServersHostProtocol::Tcp => backend::ds::HostProtocol::HostTcp, } } } -impl ApiFrom for models::ServersHostProtocol { - fn api_from(value: backend::dynamic_servers::HostProtocol) -> models::ServersHostProtocol { +impl ApiFrom for models::ServersHostProtocol { + fn api_from(value: backend::ds::HostProtocol) -> models::ServersHostProtocol { match value { - backend::dynamic_servers::HostProtocol::HostUdp => models::ServersHostProtocol::Udp, - backend::dynamic_servers::HostProtocol::HostTcp => models::ServersHostProtocol::Tcp, + backend::ds::HostProtocol::HostUdp => models::ServersHostProtocol::Udp, + backend::ds::HostProtocol::HostTcp => models::ServersHostProtocol::Tcp, } } } diff --git a/proto/backend/ds.proto b/proto/backend/ds.proto new file mode 100644 index 000000000..87f0558aa --- /dev/null +++ b/proto/backend/ds.proto @@ -0,0 +1,304 @@ +syntax = "proto3"; + +package rivet.backend.ds; + +import "proto/common.proto"; +import "proto/backend/captcha.proto"; +import "proto/backend/region.proto"; + +message Server { + rivet.common.Uuid server_id = 1; + rivet.common.Uuid game_id = 2; + rivet.common.Uuid datacenter_id = 3; + rivet.common.Uuid cluster_id = 4; + map metadata = 5; + rivet.backend.ds.ServerResources resources = 6; + repeated string args = 10; + map environment = 11; + rivet.common.Uuid image_id = 12; + NetworkMode network_mode = 13; + map network_ports = 14; + int64 kill_timeout_ms = 7; + int64 create_ts = 8; + optional int64 destroy_ts = 9; + +} + +message ServerResources { + int32 cpu_millicores = 1; + int32 memory_mib = 2; +} + +enum NetworkMode { + BRIDGE = 0; + HOST = 1; +} + +message Port { + // Null when using host networking since one is automatially assigned + optional int32 server_port = 1; + optional string public_hostname = 2; + optional int32 public_port = 3; + + oneof routing { + GameGuardRouting game_guard = 101; + HostRouting host = 102; + } +} + +message GameGuardRouting { + GameGuardProtocol protocol = 1; +} + +enum GameGuardProtocol { + HTTP = 0; + HTTPS = 1; + TCP = 2; + TCP_TLS = 3; + UDP = 4; +} + +message HostRouting { + HostProtocol protocol = 1; +} + +enum HostProtocol { + HOST_TCP = 0; + HOST_UDP = 1; +} + + +// +// +// +// // MARK: Game Config +// message GameConfig { +// bool host_networking_enabled = 1; +// bool root_user_enabled = 2; +// } +// +// // MARK: Game Namespace Config +// message NamespaceConfig { +// uint32 lobby_count_max = 1; +// uint32 max_players_per_client = 2; +// uint32 max_players_per_client_vpn = 3; +// uint32 max_players_per_client_proxy = 4; +// uint32 max_players_per_client_tor = 5; +// uint32 max_players_per_client_hosting = 6; +// } +// +// // MARK: Game Version Config +// message VersionConfig { +// repeated LobbyGroup lobby_groups = 1; +// +// optional rivet.backend.captcha.CaptchaConfig captcha = 2; +// } +// +// message LobbyGroup { +// message Region { +// rivet.common.Uuid region_id = 1; +// string tier_name_id = 2; +// IdleLobbies idle_lobbies = 3; +// } +// +// message IdleLobbies { +// uint32 min_idle_lobbies = 1; +// uint32 max_idle_lobbies = 2; +// } +// +// message Actions { +// optional FindConfig find = 1; +// optional JoinConfig join = 2; +// optional CreateConfig create = 3; +// } +// +// string name_id = 1; +// +// repeated Region regions = 101; +// uint32 max_players_normal = 102; +// uint32 max_players_direct = 103; +// uint32 max_players_party = 104; +// bool listable = 105; +// bool taggable = 106; +// bool allow_dynamic_max_players = 107; +// +// LobbyRuntime runtime = 201; +// +// optional Actions actions = 301; +// } +// +// message LobbyRuntime { +// enum NetworkMode { +// BRIDGE = 0; +// HOST = 1; +// } +// +// // Should be named "PortProtocol" +// enum ProxyProtocol { +// HTTP = 0; +// HTTPS = 1; +// TCP = 3; +// TCP_TLS = 4; +// UDP = 2; +// } +// +// enum ProxyKind { +// GAME_GUARD = 0; +// NONE = 1; +// } +// +// message PortRange { +// uint32 min = 1; +// uint32 max = 2; +// } +// +// message Port { +// string label = 1; +// +// // Only applicable to `ProxyProtocol::HTTP` and `ProxyProtocol::HTTP`. +// optional uint32 target_port = 2; +// +// // Only applicable to `ProxyProtocol::UDP` and `ProxyProtocol::TCP` when `proxy_kind` is `ProxyKind::GameGuard`. +// optional PortRange port_range = 4; +// +// ProxyProtocol proxy_protocol = 3; +// +// ProxyKind proxy_kind = 5; +// } +// +// message EnvVar { +// string key = 1; +// string value = 2; +// } +// +// message Docker { +// rivet.common.Uuid build_id = 1; +// repeated string args = 2; +// repeated EnvVar env_vars = 4; +// NetworkMode network_mode = 5; +// repeated Port ports = 3; +// } +// +// oneof runtime { +// Docker docker = 201; +// }; +// } +// +// enum IdentityRequirement { +// NONE = 0; +// GUEST = 1; +// REGISTERED = 2; +// } +// +// message VerificationConfig { +// string url = 1; +// map headers = 2; +// } +// +// message FindConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// } +// +// message JoinConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// } +// +// message CreateConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// +// bool enable_public = 4; +// bool enable_private = 5; +// +// optional uint64 max_lobbies_per_identity = 6; +// } +// +// // MARK: Game Version Config Context +// // Context required to publish a new version. +// message VersionConfigCtx { +// repeated LobbyGroupCtx lobby_groups = 1; +// } +// +// message LobbyGroupCtx { +// LobbyRuntimeCtx runtime = 101; +// } +// +// message LobbyRuntimeCtx { +// message Docker { +// optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; +// } +// +// oneof runtime { +// Docker docker = 1; +// }; +// } +// +// // MARK: Game Version Config Meta +// // Metadata about a given configuration generated after publishing. +// message VersionConfigMeta { +// repeated LobbyGroupMeta lobby_groups = 1; +// } +// +// message LobbyGroupMeta { +// // The indexes of `LobbyGroupMeta` and `LobbyGroupConfig` returned by `game-version-get` line up, so +// // fetching lobby group config via `lobby_group_id` is done via zipping. +// rivet.common.Uuid lobby_group_id = 1; +// +// LobbyRuntimeMeta runtime = 101; +// } +// +// message LobbyRuntimeMeta { +// message Docker { +// optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; +// } +// +// oneof runtime { +// Docker docker = 201; +// }; +// } +// +// // MARK: Lobby State +// message Lobby { +// enum Publicity { +// PUBLIC = 0; +// PRIVATE = 1; +// } +// +// reserved 10; +// +// rivet.common.Uuid lobby_id = 1; +// rivet.common.Uuid lobby_group_id = 2; +// rivet.common.Uuid region_id = 3; +// rivet.common.Uuid token_session_id = 4; +// int64 create_ts = 5; +// optional int64 ready_ts = 14; +// optional int64 stop_ts = 13; +// optional rivet.common.Uuid run_id = 6; +// bool is_closed = 11; +// rivet.common.Uuid namespace_id = 9; +// optional rivet.common.Uuid create_ray_id = 12; +// optional rivet.common.Uuid creator_user_id = 15; +// bool is_custom = 16; +// Publicity publicity = 17; +// +// uint32 max_players_normal = 101; +// uint32 max_players_direct = 102; +// uint32 max_players_party = 103; +// } +// +// // MARK: Player State +// message Player { +// rivet.common.Uuid player_id = 1; +// rivet.common.Uuid lobby_id = 2; +// int64 create_ts = 3; +// optional int64 register_ts = 4; +// optional int64 remove_ts = 5; +// rivet.common.Uuid token_session_id = 6; +// rivet.common.Uuid create_ray_id = 7; +// } +// diff --git a/proto/backend/dynamic_servers.proto b/proto/backend/dynamic_servers.proto deleted file mode 100644 index 3949b8e64..000000000 --- a/proto/backend/dynamic_servers.proto +++ /dev/null @@ -1,313 +0,0 @@ -syntax = "proto3"; - -package rivet.backend.dynamic_servers; - -import "proto/common.proto"; -import "proto/backend/captcha.proto"; -import "proto/backend/region.proto"; - -message Server { - rivet.common.Uuid server_id = 1; - rivet.common.Uuid game_id = 2; - rivet.common.Uuid datacenter_id = 3; - rivet.common.Uuid cluster_id = 4; - map metadata = 5; - rivet.backend.dynamic_servers.ServerResources resources = 6; - int64 kill_timeout_ms = 7; - - int64 create_ts = 8; - optional int64 destroy_ts = 9; - - oneof runtime { - rivet.backend.dynamic_servers.DockerRuntime docker_runtime = 10; - } -} - -message ServerResources { - int32 cpu_millicores = 1; - int32 memory_mib = 2; -} - -message DockerRuntime { - repeated string args = 1; - map environment = 2; - rivet.common.Uuid image_id = 3; - DockerNetwork network = 4; -} - -message DockerNetwork { - DockerNetworkMode mode = 1; - map ports = 2; -} - -enum DockerNetworkMode { - BRIDGE = 0; - HOST = 1; -} - -message DockerPort { - // Null when using host networking since one is automatially assigned - optional int32 port = 1; - - oneof routing { - DockerGameGuardRouting game_guard = 2; - DockerHostRouting host = 3; - } -} - -message DockerGameGuardRouting { - GameGuardProtocol protocol = 1; -} - -enum GameGuardProtocol { - HTTP = 0; - HTTPS = 1; - TCP = 2; - TCP_TLS = 3; - UDP = 4; -} - -message DockerHostRouting { - HostProtocol protocol = 1; -} - -enum HostProtocol { - HOST_TCP = 0; - HOST_UDP = 1; -} - - - - - -// MARK: Game Config -message GameConfig { - bool host_networking_enabled = 1; - bool root_user_enabled = 2; -} - -// MARK: Game Namespace Config -message NamespaceConfig { - uint32 lobby_count_max = 1; - uint32 max_players_per_client = 2; - uint32 max_players_per_client_vpn = 3; - uint32 max_players_per_client_proxy = 4; - uint32 max_players_per_client_tor = 5; - uint32 max_players_per_client_hosting = 6; -} - -// MARK: Game Version Config -message VersionConfig { - repeated LobbyGroup lobby_groups = 1; - - optional rivet.backend.captcha.CaptchaConfig captcha = 2; -} - -message LobbyGroup { - message Region { - rivet.common.Uuid region_id = 1; - string tier_name_id = 2; - IdleLobbies idle_lobbies = 3; - } - - message IdleLobbies { - uint32 min_idle_lobbies = 1; - uint32 max_idle_lobbies = 2; - } - - message Actions { - optional FindConfig find = 1; - optional JoinConfig join = 2; - optional CreateConfig create = 3; - } - - string name_id = 1; - - repeated Region regions = 101; - uint32 max_players_normal = 102; - uint32 max_players_direct = 103; - uint32 max_players_party = 104; - bool listable = 105; - bool taggable = 106; - bool allow_dynamic_max_players = 107; - - LobbyRuntime runtime = 201; - - optional Actions actions = 301; -} - -message LobbyRuntime { - enum NetworkMode { - BRIDGE = 0; - HOST = 1; - } - - // Should be named "PortProtocol" - enum ProxyProtocol { - HTTP = 0; - HTTPS = 1; - TCP = 3; - TCP_TLS = 4; - UDP = 2; - } - - enum ProxyKind { - GAME_GUARD = 0; - NONE = 1; - } - - message PortRange { - uint32 min = 1; - uint32 max = 2; - } - - message Port { - string label = 1; - - // Only applicable to `ProxyProtocol::HTTP` and `ProxyProtocol::HTTP`. - optional uint32 target_port = 2; - - // Only applicable to `ProxyProtocol::UDP` and `ProxyProtocol::TCP` when `proxy_kind` is `ProxyKind::GameGuard`. - optional PortRange port_range = 4; - - ProxyProtocol proxy_protocol = 3; - - ProxyKind proxy_kind = 5; - } - - message EnvVar { - string key = 1; - string value = 2; - } - - message Docker { - rivet.common.Uuid build_id = 1; - repeated string args = 2; - repeated EnvVar env_vars = 4; - NetworkMode network_mode = 5; - repeated Port ports = 3; - } - - oneof runtime { - Docker docker = 201; - }; -} - -enum IdentityRequirement { - NONE = 0; - GUEST = 1; - REGISTERED = 2; -} - -message VerificationConfig { - string url = 1; - map headers = 2; -} - -message FindConfig { - bool enabled = 1; - IdentityRequirement identity_requirement = 2; - optional VerificationConfig verification = 3; -} - -message JoinConfig { - bool enabled = 1; - IdentityRequirement identity_requirement = 2; - optional VerificationConfig verification = 3; -} - -message CreateConfig { - bool enabled = 1; - IdentityRequirement identity_requirement = 2; - optional VerificationConfig verification = 3; - - bool enable_public = 4; - bool enable_private = 5; - - optional uint64 max_lobbies_per_identity = 6; -} - -// MARK: Game Version Config Context -// Context required to publish a new version. -message VersionConfigCtx { - repeated LobbyGroupCtx lobby_groups = 1; -} - -message LobbyGroupCtx { - LobbyRuntimeCtx runtime = 101; -} - -message LobbyRuntimeCtx { - message Docker { - optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; - } - - oneof runtime { - Docker docker = 1; - }; -} - -// MARK: Game Version Config Meta -// Metadata about a given configuration generated after publishing. -message VersionConfigMeta { - repeated LobbyGroupMeta lobby_groups = 1; -} - -message LobbyGroupMeta { - // The indexes of `LobbyGroupMeta` and `LobbyGroupConfig` returned by `game-version-get` line up, so - // fetching lobby group config via `lobby_group_id` is done via zipping. - rivet.common.Uuid lobby_group_id = 1; - - LobbyRuntimeMeta runtime = 101; -} - -message LobbyRuntimeMeta { - message Docker { - optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; - } - - oneof runtime { - Docker docker = 201; - }; -} - -// MARK: Lobby State -message Lobby { - enum Publicity { - PUBLIC = 0; - PRIVATE = 1; - } - - reserved 10; - - rivet.common.Uuid lobby_id = 1; - rivet.common.Uuid lobby_group_id = 2; - rivet.common.Uuid region_id = 3; - rivet.common.Uuid token_session_id = 4; - int64 create_ts = 5; - optional int64 ready_ts = 14; - optional int64 stop_ts = 13; - optional rivet.common.Uuid run_id = 6; - bool is_closed = 11; - rivet.common.Uuid namespace_id = 9; - optional rivet.common.Uuid create_ray_id = 12; - optional rivet.common.Uuid creator_user_id = 15; - bool is_custom = 16; - Publicity publicity = 17; - - uint32 max_players_normal = 101; - uint32 max_players_direct = 102; - uint32 max_players_party = 103; -} - -// MARK: Player State -message Player { - rivet.common.Uuid player_id = 1; - rivet.common.Uuid lobby_id = 2; - int64 create_ts = 3; - optional int64 register_ts = 4; - optional int64 remove_ts = 5; - rivet.common.Uuid token_session_id = 6; - rivet.common.Uuid create_ray_id = 7; -} - diff --git a/sdks/full/go/servers/servers.go b/sdks/full/go/servers/servers.go index 5775b90e2..544597132 100644 --- a/sdks/full/go/servers/servers.go +++ b/sdks/full/go/servers/servers.go @@ -110,3 +110,32 @@ func (d *DestroyServerResponse) String() string { } return fmt.Sprintf("%#v", d) } + +type GetServerResponse struct { + Servers []*Server `json:"servers,omitempty"` + + _rawJSON json.RawMessage +} + +func (g *GetServerResponse) UnmarshalJSON(data []byte) error { + type unmarshaler GetServerResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *g = GetServerResponse(value) + g._rawJSON = json.RawMessage(data) + return nil +} + +func (g *GetServerResponse) String() string { + if len(g._rawJSON) > 0 { + if value, err := core.StringifyJSON(g._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} diff --git a/sdks/full/go/servers/servers/client.go b/sdks/full/go/servers/servers/client.go index 1affa462d..1f817e4f0 100644 --- a/sdks/full/go/servers/servers/client.go +++ b/sdks/full/go/servers/servers/client.go @@ -35,6 +35,86 @@ func NewClient(opts ...core.ClientOption) *Client { } } +// Gets a dynamic server. +// +// The id of the server to destroy +func (c *Client) Get(ctx context.Context, serverId uuid.UUID) (*servers.GetServerResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := fmt.Sprintf(baseURL+"/"+"servers/servers/%v", serverId) + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.GetServerResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + // Create a new dynamic server. func (c *Client) Create(ctx context.Context, request *servers.CreateServerRequest) (*servers.CreateServerResponse, error) { baseURL := "https://api.rivet.gg" diff --git a/sdks/full/go/servers/types.go b/sdks/full/go/servers/types.go index 33d0e84b1..4ffae63f1 100644 --- a/sdks/full/go/servers/types.go +++ b/sdks/full/go/servers/types.go @@ -173,10 +173,10 @@ func (n NetworkMode) Ptr() *NetworkMode { } type Port struct { - Routing *PortRouting `json:"routing,omitempty"` - ServerPort *int `json:"server_port,omitempty"` - PublicHost *string `json:"public_host,omitempty"` - PublicPort *int `json:"public_port,omitempty"` + Routing *PortRouting `json:"routing,omitempty"` + ServerPort *int `json:"server_port,omitempty"` + PublicHostname *string `json:"public_hostname,omitempty"` + PublicPort *int `json:"public_port,omitempty"` _rawJSON json.RawMessage } diff --git a/sdks/full/openapi/openapi.yml b/sdks/full/openapi/openapi.yml index 4a2126a84..b0dfc87a8 100644 --- a/sdks/full/openapi/openapi.yml +++ b/sdks/full/openapi/openapi.yml @@ -8905,20 +8905,27 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 - /servers/servers: - post: - description: Create a new dynamic server. - operationId: servers_servers_create + /servers/servers/{server_id}: + get: + description: Gets a dynamic server. + operationId: servers_servers_get tags: - ServersServers - parameters: [] + parameters: + - name: server_id + in: path + description: The id of the server to destroy + required: true + schema: + type: string + format: uuid responses: '200': description: '' content: application/json: schema: - $ref: '#/components/schemas/ServersCreateServerResponse' + $ref: '#/components/schemas/ServersGetServerResponse' '400': description: '' content: @@ -8956,13 +8963,6 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ServersCreateServerRequest' - /servers/servers/{server_id}: delete: description: Destroy a dynamic server. operationId: servers_servers_destroy @@ -9030,6 +9030,63 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 + /servers/servers: + post: + description: Create a new dynamic server. + operationId: servers_servers_create + tags: + - ServersServers + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerRequest' components: schemas: AdminLoginRequest: @@ -13648,7 +13705,7 @@ components: $ref: '#/components/schemas/ServersPortRouting' server_port: type: integer - public_host: + public_hostname: type: string public_port: type: integer @@ -13684,6 +13741,15 @@ components: enum: - tcp - udp + ServersGetServerResponse: + type: object + properties: + servers: + type: array + items: + $ref: '#/components/schemas/ServersServer' + required: + - servers ServersCreateServerRequest: type: object properties: diff --git a/sdks/full/openapi_compat/openapi.yml b/sdks/full/openapi_compat/openapi.yml index 5e28f76e6..a58193afe 100644 --- a/sdks/full/openapi_compat/openapi.yml +++ b/sdks/full/openapi_compat/openapi.yml @@ -4480,6 +4480,15 @@ components: protocol: $ref: '#/components/schemas/ServersGameGuardProtocol' type: object + ServersGetServerResponse: + properties: + servers: + items: + $ref: '#/components/schemas/ServersServer' + type: array + required: + - servers + type: object ServersHostProtocol: enum: - tcp @@ -4508,7 +4517,7 @@ components: type: string ServersPort: properties: - public_host: + public_hostname: type: string public_port: type: integer @@ -13547,6 +13556,63 @@ paths: security: *id001 tags: - ServersServers + get: + description: Gets a dynamic server. + operationId: servers_servers_get + parameters: + - description: The id of the server to destroy + in: path + name: server_id + required: true + schema: + format: uuid + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersGetServerResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - ServersServers /servers/{ip}: get: operationId: provision_servers_getInfo diff --git a/sdks/full/rust-cli/.openapi-generator/FILES b/sdks/full/rust-cli/.openapi-generator/FILES index 88b09a187..c37a6cc35 100644 --- a/sdks/full/rust-cli/.openapi-generator/FILES +++ b/sdks/full/rust-cli/.openapi-generator/FILES @@ -353,6 +353,7 @@ docs/ServersCreateServerResponse.md docs/ServersDestroyServerResponse.md docs/ServersGameGuardProtocol.md docs/ServersGameGuardRouting.md +docs/ServersGetServerResponse.md docs/ServersHostProtocol.md docs/ServersHostRouting.md docs/ServersNetwork.md @@ -722,6 +723,7 @@ src/models/servers_create_server_response.rs src/models/servers_destroy_server_response.rs src/models/servers_game_guard_protocol.rs src/models/servers_game_guard_routing.rs +src/models/servers_get_server_response.rs src/models/servers_host_protocol.rs src/models/servers_host_routing.rs src/models/servers_network.rs diff --git a/sdks/full/rust-cli/README.md b/sdks/full/rust-cli/README.md index bbe2eda46..fc349b50f 100644 --- a/sdks/full/rust-cli/README.md +++ b/sdks/full/rust-cli/README.md @@ -169,6 +169,7 @@ Class | Method | HTTP request | Description *ProvisionServersApi* | [**provision_servers_get_info**](docs/ProvisionServersApi.md#provision_servers_get_info) | **GET** /servers/{ip} | *ServersServersApi* | [**servers_servers_create**](docs/ServersServersApi.md#servers_servers_create) | **POST** /servers/servers | *ServersServersApi* | [**servers_servers_destroy**](docs/ServersServersApi.md#servers_servers_destroy) | **DELETE** /servers/servers/{server_id} | +*ServersServersApi* | [**servers_servers_get**](docs/ServersServersApi.md#servers_servers_get) | **GET** /servers/servers/{server_id} | ## Documentation For Models @@ -484,6 +485,7 @@ Class | Method | HTTP request | Description - [ServersDestroyServerResponse](docs/ServersDestroyServerResponse.md) - [ServersGameGuardProtocol](docs/ServersGameGuardProtocol.md) - [ServersGameGuardRouting](docs/ServersGameGuardRouting.md) + - [ServersGetServerResponse](docs/ServersGetServerResponse.md) - [ServersHostProtocol](docs/ServersHostProtocol.md) - [ServersHostRouting](docs/ServersHostRouting.md) - [ServersNetwork](docs/ServersNetwork.md) diff --git a/sdks/full/rust-cli/docs/ServersGetServerResponse.md b/sdks/full/rust-cli/docs/ServersGetServerResponse.md new file mode 100644 index 000000000..861a5e783 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersGetServerResponse.md @@ -0,0 +1,11 @@ +# ServersGetServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**servers** | [**Vec**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersPort.md b/sdks/full/rust-cli/docs/ServersPort.md index ef0143059..1991749b0 100644 --- a/sdks/full/rust-cli/docs/ServersPort.md +++ b/sdks/full/rust-cli/docs/ServersPort.md @@ -4,7 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**public_host** | Option<**String**> | | [optional] +**public_hostname** | Option<**String**> | | [optional] **public_port** | Option<**i32**> | | [optional] **routing** | [**crate::models::ServersPortRouting**](ServersPortRouting.md) | | **server_port** | Option<**i32**> | | [optional] diff --git a/sdks/full/rust-cli/docs/ServersServersApi.md b/sdks/full/rust-cli/docs/ServersServersApi.md index 216b1035c..3bc24a18a 100644 --- a/sdks/full/rust-cli/docs/ServersServersApi.md +++ b/sdks/full/rust-cli/docs/ServersServersApi.md @@ -6,6 +6,7 @@ Method | HTTP request | Description ------------- | ------------- | ------------- [**servers_servers_create**](ServersServersApi.md#servers_servers_create) | **POST** /servers/servers | [**servers_servers_destroy**](ServersServersApi.md#servers_servers_destroy) | **DELETE** /servers/servers/{server_id} | +[**servers_servers_get**](ServersServersApi.md#servers_servers_get) | **GET** /servers/servers/{server_id} | @@ -69,3 +70,33 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## servers_servers_get + +> crate::models::ServersGetServerResponse servers_servers_get(server_id) + + +Gets a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | + +### Return type + +[**crate::models::ServersGetServerResponse**](ServersGetServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust-cli/src/apis/servers_servers_api.rs b/sdks/full/rust-cli/src/apis/servers_servers_api.rs index 48f38ff72..38f2b861f 100644 --- a/sdks/full/rust-cli/src/apis/servers_servers_api.rs +++ b/sdks/full/rust-cli/src/apis/servers_servers_api.rs @@ -41,6 +41,19 @@ pub enum ServersServersDestroyError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`servers_servers_get`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersServersGetError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + /// Create a new dynamic server. pub async fn servers_servers_create(configuration: &configuration::Configuration, servers_create_server_request: crate::models::ServersCreateServerRequest) -> Result> { @@ -108,3 +121,34 @@ pub async fn servers_servers_destroy(configuration: &configuration::Configuratio } } +/// Gets a dynamic server. +pub async fn servers_servers_get(configuration: &configuration::Configuration, server_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust-cli/src/models/mod.rs b/sdks/full/rust-cli/src/models/mod.rs index f86ead99e..678843ea2 100644 --- a/sdks/full/rust-cli/src/models/mod.rs +++ b/sdks/full/rust-cli/src/models/mod.rs @@ -620,6 +620,8 @@ pub mod servers_game_guard_protocol; pub use self::servers_game_guard_protocol::ServersGameGuardProtocol; pub mod servers_game_guard_routing; pub use self::servers_game_guard_routing::ServersGameGuardRouting; +pub mod servers_get_server_response; +pub use self::servers_get_server_response::ServersGetServerResponse; pub mod servers_host_protocol; pub use self::servers_host_protocol::ServersHostProtocol; pub mod servers_host_routing; diff --git a/sdks/full/rust-cli/src/models/servers_get_server_response.rs b/sdks/full/rust-cli/src/models/servers_get_server_response.rs new file mode 100644 index 000000000..ff13db426 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_get_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersGetServerResponse { + #[serde(rename = "servers")] + pub servers: Vec, +} + +impl ServersGetServerResponse { + pub fn new(servers: Vec) -> ServersGetServerResponse { + ServersGetServerResponse { + servers, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_port.rs b/sdks/full/rust-cli/src/models/servers_port.rs index 8937413ed..069a8a222 100644 --- a/sdks/full/rust-cli/src/models/servers_port.rs +++ b/sdks/full/rust-cli/src/models/servers_port.rs @@ -13,8 +13,8 @@ #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] pub struct ServersPort { - #[serde(rename = "public_host", skip_serializing_if = "Option::is_none")] - pub public_host: Option, + #[serde(rename = "public_hostname", skip_serializing_if = "Option::is_none")] + pub public_hostname: Option, #[serde(rename = "public_port", skip_serializing_if = "Option::is_none")] pub public_port: Option, #[serde(rename = "routing")] @@ -26,7 +26,7 @@ pub struct ServersPort { impl ServersPort { pub fn new(routing: crate::models::ServersPortRouting) -> ServersPort { ServersPort { - public_host: None, + public_hostname: None, public_port: None, routing: Box::new(routing), server_port: None, diff --git a/sdks/full/rust/.openapi-generator/FILES b/sdks/full/rust/.openapi-generator/FILES index 88b09a187..c37a6cc35 100644 --- a/sdks/full/rust/.openapi-generator/FILES +++ b/sdks/full/rust/.openapi-generator/FILES @@ -353,6 +353,7 @@ docs/ServersCreateServerResponse.md docs/ServersDestroyServerResponse.md docs/ServersGameGuardProtocol.md docs/ServersGameGuardRouting.md +docs/ServersGetServerResponse.md docs/ServersHostProtocol.md docs/ServersHostRouting.md docs/ServersNetwork.md @@ -722,6 +723,7 @@ src/models/servers_create_server_response.rs src/models/servers_destroy_server_response.rs src/models/servers_game_guard_protocol.rs src/models/servers_game_guard_routing.rs +src/models/servers_get_server_response.rs src/models/servers_host_protocol.rs src/models/servers_host_routing.rs src/models/servers_network.rs diff --git a/sdks/full/rust/README.md b/sdks/full/rust/README.md index bbe2eda46..fc349b50f 100644 --- a/sdks/full/rust/README.md +++ b/sdks/full/rust/README.md @@ -169,6 +169,7 @@ Class | Method | HTTP request | Description *ProvisionServersApi* | [**provision_servers_get_info**](docs/ProvisionServersApi.md#provision_servers_get_info) | **GET** /servers/{ip} | *ServersServersApi* | [**servers_servers_create**](docs/ServersServersApi.md#servers_servers_create) | **POST** /servers/servers | *ServersServersApi* | [**servers_servers_destroy**](docs/ServersServersApi.md#servers_servers_destroy) | **DELETE** /servers/servers/{server_id} | +*ServersServersApi* | [**servers_servers_get**](docs/ServersServersApi.md#servers_servers_get) | **GET** /servers/servers/{server_id} | ## Documentation For Models @@ -484,6 +485,7 @@ Class | Method | HTTP request | Description - [ServersDestroyServerResponse](docs/ServersDestroyServerResponse.md) - [ServersGameGuardProtocol](docs/ServersGameGuardProtocol.md) - [ServersGameGuardRouting](docs/ServersGameGuardRouting.md) + - [ServersGetServerResponse](docs/ServersGetServerResponse.md) - [ServersHostProtocol](docs/ServersHostProtocol.md) - [ServersHostRouting](docs/ServersHostRouting.md) - [ServersNetwork](docs/ServersNetwork.md) diff --git a/sdks/full/rust/docs/ServersGetServerResponse.md b/sdks/full/rust/docs/ServersGetServerResponse.md new file mode 100644 index 000000000..861a5e783 --- /dev/null +++ b/sdks/full/rust/docs/ServersGetServerResponse.md @@ -0,0 +1,11 @@ +# ServersGetServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**servers** | [**Vec**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersPort.md b/sdks/full/rust/docs/ServersPort.md index ef0143059..1991749b0 100644 --- a/sdks/full/rust/docs/ServersPort.md +++ b/sdks/full/rust/docs/ServersPort.md @@ -4,7 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**public_host** | Option<**String**> | | [optional] +**public_hostname** | Option<**String**> | | [optional] **public_port** | Option<**i32**> | | [optional] **routing** | [**crate::models::ServersPortRouting**](ServersPortRouting.md) | | **server_port** | Option<**i32**> | | [optional] diff --git a/sdks/full/rust/docs/ServersServersApi.md b/sdks/full/rust/docs/ServersServersApi.md index 216b1035c..3bc24a18a 100644 --- a/sdks/full/rust/docs/ServersServersApi.md +++ b/sdks/full/rust/docs/ServersServersApi.md @@ -6,6 +6,7 @@ Method | HTTP request | Description ------------- | ------------- | ------------- [**servers_servers_create**](ServersServersApi.md#servers_servers_create) | **POST** /servers/servers | [**servers_servers_destroy**](ServersServersApi.md#servers_servers_destroy) | **DELETE** /servers/servers/{server_id} | +[**servers_servers_get**](ServersServersApi.md#servers_servers_get) | **GET** /servers/servers/{server_id} | @@ -69,3 +70,33 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## servers_servers_get + +> crate::models::ServersGetServerResponse servers_servers_get(server_id) + + +Gets a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | + +### Return type + +[**crate::models::ServersGetServerResponse**](ServersGetServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust/src/apis/servers_servers_api.rs b/sdks/full/rust/src/apis/servers_servers_api.rs index 48f38ff72..38f2b861f 100644 --- a/sdks/full/rust/src/apis/servers_servers_api.rs +++ b/sdks/full/rust/src/apis/servers_servers_api.rs @@ -41,6 +41,19 @@ pub enum ServersServersDestroyError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`servers_servers_get`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersServersGetError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + /// Create a new dynamic server. pub async fn servers_servers_create(configuration: &configuration::Configuration, servers_create_server_request: crate::models::ServersCreateServerRequest) -> Result> { @@ -108,3 +121,34 @@ pub async fn servers_servers_destroy(configuration: &configuration::Configuratio } } +/// Gets a dynamic server. +pub async fn servers_servers_get(configuration: &configuration::Configuration, server_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust/src/models/mod.rs b/sdks/full/rust/src/models/mod.rs index f86ead99e..678843ea2 100644 --- a/sdks/full/rust/src/models/mod.rs +++ b/sdks/full/rust/src/models/mod.rs @@ -620,6 +620,8 @@ pub mod servers_game_guard_protocol; pub use self::servers_game_guard_protocol::ServersGameGuardProtocol; pub mod servers_game_guard_routing; pub use self::servers_game_guard_routing::ServersGameGuardRouting; +pub mod servers_get_server_response; +pub use self::servers_get_server_response::ServersGetServerResponse; pub mod servers_host_protocol; pub use self::servers_host_protocol::ServersHostProtocol; pub mod servers_host_routing; diff --git a/sdks/full/rust/src/models/servers_get_server_response.rs b/sdks/full/rust/src/models/servers_get_server_response.rs new file mode 100644 index 000000000..ff13db426 --- /dev/null +++ b/sdks/full/rust/src/models/servers_get_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersGetServerResponse { + #[serde(rename = "servers")] + pub servers: Vec, +} + +impl ServersGetServerResponse { + pub fn new(servers: Vec) -> ServersGetServerResponse { + ServersGetServerResponse { + servers, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_port.rs b/sdks/full/rust/src/models/servers_port.rs index 8937413ed..069a8a222 100644 --- a/sdks/full/rust/src/models/servers_port.rs +++ b/sdks/full/rust/src/models/servers_port.rs @@ -13,8 +13,8 @@ #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] pub struct ServersPort { - #[serde(rename = "public_host", skip_serializing_if = "Option::is_none")] - pub public_host: Option, + #[serde(rename = "public_hostname", skip_serializing_if = "Option::is_none")] + pub public_hostname: Option, #[serde(rename = "public_port", skip_serializing_if = "Option::is_none")] pub public_port: Option, #[serde(rename = "routing")] @@ -26,7 +26,7 @@ pub struct ServersPort { impl ServersPort { pub fn new(routing: crate::models::ServersPortRouting) -> ServersPort { ServersPort { - public_host: None, + public_hostname: None, public_port: None, routing: Box::new(routing), server_port: None, diff --git a/sdks/full/typescript/src/api/resources/servers/resources/servers/client/Client.ts b/sdks/full/typescript/src/api/resources/servers/resources/servers/client/Client.ts index 993df3bfa..d9bbc9130 100644 --- a/sdks/full/typescript/src/api/resources/servers/resources/servers/client/Client.ts +++ b/sdks/full/typescript/src/api/resources/servers/resources/servers/client/Client.ts @@ -5,8 +5,8 @@ import * as environments from "../../../../../../environments"; import * as core from "../../../../../../core"; import * as Rivet from "../../../../.."; -import * as serializers from "../../../../../../serialization"; import urlJoin from "url-join"; +import * as serializers from "../../../../../../serialization"; import * as errors from "../../../../../../errors"; export declare namespace Servers { @@ -25,6 +25,127 @@ export declare namespace Servers { export class Servers { constructor(protected readonly _options: Servers.Options = {}) {} + /** + * Gets a dynamic server. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async get( + serverId: string, + requestOptions?: Servers.RequestOptions + ): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + `/servers/servers/${serverId}` + ), + method: "GET", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.GetServerResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + /** * Create a new dynamic server. * @throws {@link Rivet.InternalError} diff --git a/svc/Cargo.lock b/svc/Cargo.lock index 36c859341..3b512284c 100644 --- a/svc/Cargo.lock +++ b/svc/Cargo.lock @@ -819,6 +819,7 @@ dependencies = [ "cluster-resolve-for-name-id", "ds-server-create", "ds-server-delete", + "ds-server-get", "http 0.2.12", "hyper", "rivet-api", @@ -919,6 +920,7 @@ dependencies = [ "rivet-pools", "rivet-route", "rivet-util-cdn", + "rivet-util-ds", "rivet-util-job", "s3-util", "serde", @@ -3175,6 +3177,7 @@ dependencies = [ "rivet-operation", "rivet-util", "rivet-util-build", + "rivet-util-ds", "s3-util", "serde", "serde_json", @@ -3203,6 +3206,22 @@ dependencies = [ "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] +[[package]] +name = "ds-server-get" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "cluster-list", + "ds-server-create", + "faker-build", + "faker-game", + "faker-region", + "rivet-operation", + "rivet-util-ds", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", +] + [[package]] name = "ds-worker" version = "0.0.1" @@ -7724,6 +7743,26 @@ dependencies = [ "uuid", ] +[[package]] +name = "rivet-util-ds" +version = "0.1.0" +dependencies = [ + "bit-vec", + "chirp-client", + "heck 0.3.3", + "http 0.2.12", + "ip-info", + "mm-lobby-list-for-user-id", + "region-get", + "rivet-operation", + "rivet-util", + "serde", + "serde_json", + "strum 0.24.1", + "user-identity-get", + "uuid", +] + [[package]] name = "rivet-util-env" version = "0.1.0" diff --git a/svc/Cargo.toml b/svc/Cargo.toml index cf5cd64e5..a6331e520 100644 --- a/svc/Cargo.toml +++ b/svc/Cargo.toml @@ -85,6 +85,7 @@ members = [ "pkg/debug/ops/email-res", "pkg/ds/ops/server-create", "pkg/ds/ops/server-delete", + "pkg/ds/ops/server-get", "pkg/ds/worker", "pkg/email-verification/ops/complete", "pkg/email-verification/ops/create", diff --git a/svc/api/group/src/route/groups.rs b/svc/api/group/src/route/groups.rs index 7b545ce0d..b2592f4b9 100644 --- a/svc/api/group/src/route/groups.rs +++ b/svc/api/group/src/route/groups.rs @@ -973,11 +973,11 @@ pub async fn create_invite( ); if let Some(ttl) = body.ttl { - ensure!(ttl >= 0, "invalid parameter `ttl`"); + ensure!(ttl > 0, "invalid parameter `ttl`"); ensure!(ttl <= util::duration::days(30), "parameter `ttl` too large"); } if let Some(use_count) = body.use_count { - ensure!(use_count >= 0, "invalid parameter `use_count`"); + ensure!(use_count > 0, "invalid parameter `use_count`"); ensure!(use_count <= 5000, "parameter `use_count` too large"); } diff --git a/svc/api/servers/Cargo.toml b/svc/api/servers/Cargo.toml index b4360579e..4bf7717c7 100644 --- a/svc/api/servers/Cargo.toml +++ b/svc/api/servers/Cargo.toml @@ -36,6 +36,7 @@ cluster-get-for-game = { path = "../../pkg/cluster/ops/get-for-game" } cluster-list = { path = "../../pkg/cluster/ops/list" } cluster-resolve-for-name-id = { path = "../../pkg/cluster/ops/resolve-for-name-id" } ds-server-create = { path = "../../pkg/ds/ops/server-create" } +ds-server-get = { path = "../../pkg/ds/ops/server-get" } ds-server-delete = { path = "../../pkg/ds/ops/server-delete" } [dev-dependencies] diff --git a/svc/api/servers/src/route/mod.rs b/svc/api/servers/src/route/mod.rs index b82293698..2921510a1 100644 --- a/svc/api/servers/src/route/mod.rs +++ b/svc/api/servers/src/route/mod.rs @@ -26,6 +26,10 @@ define_router! { ), }, + "servers" / Uuid : { + GET: servers::get(), + }, + "servers" / Uuid : { DELETE: servers::destroy( query: servers::DeleteQuery, diff --git a/svc/api/servers/src/route/servers.rs b/svc/api/servers/src/route/servers.rs index 2b1eea7c5..d095fc2cd 100644 --- a/svc/api/servers/src/route/servers.rs +++ b/svc/api/servers/src/route/servers.rs @@ -1,5 +1,5 @@ -use api_helper::ctx::Ctx; -use proto::backend; +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use proto::backend::{self, pkg::dynamic_servers}; use rivet_api::models; use rivet_convert::{ApiFrom, ApiInto, ApiTryInto}; use rivet_operation::prelude::*; @@ -8,6 +8,34 @@ use std::collections::HashMap; use crate::auth::Auth; +// MARK: GET /servers/{} +pub async fn get( + ctx: Ctx, + server_id: Uuid, + watch_index: WatchIndexQuery, +) -> GlobalResult { + let game_id = ctx.auth().server()?.game_id; + let games = op!([ctx] cluster_get_for_game { + game_ids: vec![game_id.into()] + }) + .await? + .games; + + // Get the server + let get_res = op!([ctx] ds_server_get { + server_ids: vec![server_id.into()], + }) + .await?; + + let servers = get_res + .servers + .iter() + .map(|server| server.clone().api_try_into()) + .collect::>>()?; + + Ok(models::ServersGetServerResponse { servers }) +} + // MARK: POST /servers pub async fn create( ctx: Ctx, @@ -44,25 +72,26 @@ pub async fn create( resources: Some((*body.resources).api_into()), kill_timeout_ms: body.kill_timeout.unwrap_or_default(), metadata: metadata, - // TODO: Flatten this - runtime: Some(backend::pkg::dynamic_servers::server_create::request::Runtime::DockerRuntime(backend::dynamic_servers::DockerRuntime { - args: body.args.unwrap_or_default(), - environment: body.environment.unwrap_or_default(), - image_id: Some(body.image_id.into()), - network: Some(backend::dynamic_servers::DockerNetwork { - mode: backend::dynamic_servers::DockerNetworkMode::api_from( - body.network.mode.unwrap_or_default(), - ) as i32, - ports: unwrap!(body.network - .ports - .into_iter() - .map(|(s, p)| Ok((s, backend::dynamic_servers::DockerPort { - port: p.server_port, - routing: Some((*p.routing).api_try_into()?), - }))) - .collect::>>()), - }), - })), + args: body.args.unwrap_or_default(), + environment: body.environment.unwrap_or_default(), + image_id: Some(body.image_id.into()), + network_mode: backend::ds::NetworkMode::api_from( + body.network.mode.unwrap_or_default(), + ) as i32, + network_ports: unwrap!(body.network + .ports + .into_iter() + .map(|(s, p)| Ok((s, dynamic_servers::server_create::Port { + server_port: p.server_port, + routing: Some(match *p.routing { + models::ServersPortRouting { game_guard: Some(x), host: None } => dynamic_servers::server_create::port::Routing::GameGuard((*x).api_into()), + models::ServersPortRouting { game_guard: None, host: Some(x), } => dynamic_servers::server_create::port::Routing::Host((*x).api_into()), + // TODO: Clean up this error + models::ServersPortRouting { .. } => bail!("must specify only game_guard or host routing") + + }) + }))) + .collect::>>()), }) .await? .server; diff --git a/svc/api/traefik-provider/Cargo.toml b/svc/api/traefik-provider/Cargo.toml index 76a9bec19..1fbf61dc7 100644 --- a/svc/api/traefik-provider/Cargo.toml +++ b/svc/api/traefik-provider/Cargo.toml @@ -33,6 +33,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ "ansi", ] } url = "2.2.2" +util-ds = { package = "rivet-util-ds", path = "../../pkg/ds/util" } util-cdn = { package = "rivet-util-cdn", path = "../../pkg/cdn/util" } util-job = { package = "rivet-util-job", path = "../../pkg/job/util" } uuid = { version = "1", features = ["v4"] } diff --git a/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs b/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs index d396b0d6a..950c85303 100644 --- a/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs +++ b/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs @@ -37,14 +37,7 @@ impl DynamicServer { } fn hostname(&self) -> GlobalResult { - // TODO: Change lobby -> server - Ok(format!( - "{}-{}.lobby.{}.{}", - self.server_id, - self.port_name, - self.datacenter_id, - unwrap!(util::env::domain_job()), - )) + util_ds::build_ds_hostname(self.server_id, &self.port_name, self.datacenter_id) } } @@ -163,14 +156,14 @@ fn ds_register_proxied_port( let ingress_port = proxied_port.gg_port.clone(); let target_nomad_port_label = proxied_port.label.clone(); let service_id = format!("ds-run:{}:{}", run_id, target_nomad_port_label); - let proxy_protocol = unwrap!(backend::dynamic_servers::GameGuardProtocol::from_i32( + let proxy_protocol = unwrap!(backend::ds::GameGuardProtocol::from_i32( proxied_port.protocol as i32 )); // Insert the relevant service match proxy_protocol { - backend::dynamic_servers::GameGuardProtocol::Http - | backend::dynamic_servers::GameGuardProtocol::Https => { + backend::ds::GameGuardProtocol::Http + | backend::ds::GameGuardProtocol::Https => { config.http.services.insert( service_id.clone(), types::TraefikService { @@ -187,8 +180,8 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::Tcp - | backend::dynamic_servers::GameGuardProtocol::TcpTls => { + backend::ds::GameGuardProtocol::Tcp + | backend::ds::GameGuardProtocol::TcpTls => { config.tcp.services.insert( service_id.clone(), types::TraefikService { @@ -205,7 +198,7 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::Udp => { + backend::ds::GameGuardProtocol::Udp => { config.udp.services.insert( service_id.clone(), types::TraefikService { @@ -226,7 +219,7 @@ fn ds_register_proxied_port( // Insert the relevant router match proxy_protocol { - backend::dynamic_servers::GameGuardProtocol::Http => { + backend::ds::GameGuardProtocol::Http => { // Generate config let middlewares = http_router_middlewares(); let rule = format_http_rule(proxied_port)?; @@ -249,7 +242,7 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::Https => { + backend::ds::GameGuardProtocol::Https => { // Generate config let middlewares = http_router_middlewares(); let rule = format_http_rule(proxied_port)?; @@ -272,7 +265,7 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::Tcp => { + backend::ds::GameGuardProtocol::Tcp => { config.tcp.routers.insert( format!("ds-run:{}:{}:tcp", run_id, target_nomad_port_label), types::TraefikRouter { @@ -285,7 +278,7 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::TcpTls => { + backend::ds::GameGuardProtocol::TcpTls => { config.tcp.routers.insert( format!("ds-run:{}:{}:tcp-tls", run_id, target_nomad_port_label), types::TraefikRouter { @@ -298,7 +291,7 @@ fn ds_register_proxied_port( }, ); } - backend::dynamic_servers::GameGuardProtocol::Udp => { + backend::ds::GameGuardProtocol::Udp => { config.udp.routers.insert( format!("ds-run:{}:{}:udp", run_id, target_nomad_port_label), types::TraefikRouter { diff --git a/svc/api/traefik-provider/src/route/game_guard/mod.rs b/svc/api/traefik-provider/src/route/game_guard/mod.rs index 061374ca2..e12012dec 100644 --- a/svc/api/traefik-provider/src/route/game_guard/mod.rs +++ b/svc/api/traefik-provider/src/route/game_guard/mod.rs @@ -37,7 +37,7 @@ pub async fn config( // Fetch configs and catch any errors tracing::info!(?config, "traefik config ds"); tracing::info!("asdgaerwvsdfvasdf"); - + build_ds(&ctx, datacenter, &mut config).await?; build_job(&ctx, datacenter, &mut config).await?; diff --git a/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql b/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql index 7322aaa4f..15d9d4728 100644 --- a/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql +++ b/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql @@ -21,7 +21,7 @@ CREATE TABLE servers ( -- Docker image_id UUID NOT NULL, args STRING[], - network_mode INT NOT NULL, -- rivet.backend.dynamic_servers.DockerNetworkMode + network_mode INT NOT NULL, -- rivet.backend.ds.DockerNetworkMode -- This is a map environment JSONB NOT NULL, @@ -34,7 +34,7 @@ CREATE TABLE docker_ports_protocol_game_guard ( port_name STRING NOT NULL, port_number INT NOT NULL, gg_port INT NOT NULL, - protocol INT NOT NULL, -- rivet.backend.dynamic_servers.GameGuardProtocol + protocol INT NOT NULL, -- rivet.backend.ds.GameGuardProtocol PRIMARY KEY (server_id, port_name) ); @@ -43,7 +43,7 @@ CREATE TABLE docker_ports_host ( server_id UUID NOT NULL REFERENCES servers, port_name STRING NOT NULL, port_number INT NOT NULL, - protocol INT NOT NULL, -- rivet.backend.dynamic_servers.HostProtocol + protocol INT NOT NULL, -- rivet.backend.ds.HostProtocol PRIMARY KEY (server_id, port_name) ); diff --git a/svc/pkg/ds/ops/server-create/Cargo.toml b/svc/pkg/ds/ops/server-create/Cargo.toml index 45ec8cf01..96d3f2548 100644 --- a/svc/pkg/ds/ops/server-create/Cargo.toml +++ b/svc/pkg/ds/ops/server-create/Cargo.toml @@ -24,6 +24,7 @@ rivet-util = { path = "../../../../../lib/util/core" } heck = "0.3" s3-util = { path = "../../../../../lib/s3-util" } util-build = { package = "rivet-util-build", path = "../../../build/util" } +util-ds = { package = "rivet-util-ds", path = "../../util" } regex = "1.10" rand = "0.8" reqwest = "0.11" diff --git a/svc/pkg/ds/ops/server-create/src/lib.rs b/svc/pkg/ds/ops/server-create/src/lib.rs index bec04c5b1..78f49fd51 100644 --- a/svc/pkg/ds/ops/server-create/src/lib.rs +++ b/svc/pkg/ds/ops/server-create/src/lib.rs @@ -6,9 +6,7 @@ use nomad_job::{ escape_go_template, gen_oci_bundle_config, inject_consul_env_template, nomad_host_port_env_var, template_env_var, template_env_var_int, DecodedPort, ProxyProtocol, TransportProtocol, }; -use proto::backend::{ - self, dynamic_servers::lobby_runtime::NetworkMode as LobbyRuntimeNetworkMode, -}; +use proto::backend::{self}; use proto::{backend::pkg::*, chirp::response::Ok}; use rand::Rng; use regex::Regex; @@ -18,13 +16,11 @@ use sha2::{Digest, Sha256}; use std::hash::Hasher; use std::{collections::HashMap, hash::DefaultHasher, net::IpAddr, time::Duration}; use team::member_get::request; -use util_mm::key::lobby_config; mod nomad_job; mod oci_config; mod seccomp; mod util_job; -mod util_mm; lazy_static::lazy_static! { pub static ref NEW_NOMAD_CONFIG: nomad_client::apis::configuration::Configuration = @@ -120,8 +116,6 @@ pub async fn handle( ctx: OperationContext, ) -> GlobalResult { let resources = unwrap_ref!(ctx.resources).clone(); - let runtime = unwrap!(ctx.runtime.clone()); - let server_id = Uuid::new_v4(); let game_id = unwrap_ref!(ctx.game_id).as_uuid(); let cluster_id = unwrap_ref!(ctx.cluster_id).as_uuid(); @@ -131,66 +125,55 @@ pub async fn handle( // MARK: db insert - match runtime.clone() { - dynamic_servers::server_create::request::Runtime::DockerRuntime(docker_runtime) => { - #[derive(Default, Clone)] - struct GameGuardUnnest { - port_names: Vec, - port_numbers: Vec>, - gg_ports: Vec>, - protocols: Vec, - } + #[derive(Default, Clone)] + struct GameGuardUnnest { + port_names: Vec, + port_numbers: Vec>, + gg_ports: Vec>, + protocols: Vec, + } - #[derive(Default, Clone)] - struct HostUnnest { - port_names: Vec, - port_numbers: Vec>, - } + #[derive(Default, Clone)] + struct HostUnnest { + port_names: Vec, + port_numbers: Vec>, + } - let docker_runtime_clone = docker_runtime.clone(); - - let docker_network = unwrap!(docker_runtime.network); - - let mut game_guard_unnest = GameGuardUnnest::default(); - let mut host_unnest = HostUnnest::default(); - - for (name, port) in docker_network.ports.iter() { - let routing = unwrap!(port.routing.clone()); - match routing { - backend::dynamic_servers::docker_port::Routing::GameGuard( - gameguard_protocol, - ) => { - game_guard_unnest.port_names.push(name.clone()); - game_guard_unnest.port_numbers.push(port.port); - game_guard_unnest.gg_ports.push(match port.port { - Some(port) => Some( - choose_ingress_port(ctx.clone(), port, gameguard_protocol.protocol) - .await?, - ), - None => None, - }); - game_guard_unnest - .protocols - .push(gameguard_protocol.protocol); - } - backend::dynamic_servers::docker_port::Routing::Host(_) => { - host_unnest.port_names.push(name.clone()); - host_unnest.port_numbers.push(port.port); - } - }; + let mut game_guard_unnest = GameGuardUnnest::default(); + let mut host_unnest = HostUnnest::default(); + + for (name, port) in ctx.network_ports.iter() { + let routing = unwrap!(port.routing.clone()); + match routing { + dynamic_servers::server_create::port::Routing::GameGuard(gameguard_protocol) => { + game_guard_unnest.port_names.push(name.clone()); + game_guard_unnest.port_numbers.push(port.server_port); + game_guard_unnest.gg_ports.push(match port.server_port { + Some(port) => Some( + choose_ingress_port(ctx.clone(), port, gameguard_protocol.protocol).await?, + ), + None => None, + }); + game_guard_unnest + .protocols + .push(gameguard_protocol.protocol); } + dynamic_servers::server_create::port::Routing::Host(_) => { + host_unnest.port_names.push(name.clone()); + host_unnest.port_numbers.push(port.server_port); + } + }; + } - rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { - let ctx = ctx.clone(); - let runtime = runtime.clone(); - let docker_runtime_clone = docker_runtime_clone.clone(); - let host_unnest = host_unnest.clone(); - let game_guard_unnest = game_guard_unnest.clone(); - - async move { - sql_execute!( - [ctx, @tx tx] - " + rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + let ctx = ctx.clone(); + let host_unnest = host_unnest.clone(); + let game_guard_unnest = game_guard_unnest.clone(); + + async move { + sql_execute!( + [ctx, @tx tx] + " WITH servers_cte AS ( INSERT INTO @@ -249,33 +232,31 @@ pub async fn handle( SELECT 1 ", - server_id, - game_id, - datacenter_id, - cluster_id, - serde_json::value::to_raw_value(&ctx.metadata.to_owned())?.to_string(), // 5 - resources.cpu_millicores, - resources.memory_mib, - ctx.kill_timeout_ms, - create_ts, - unwrap!(docker_runtime_clone.image_id).as_uuid(), // 10 - docker_runtime_clone.args, - unwrap!(docker_runtime_clone.network).mode, - serde_json::value::to_raw_value(&docker_runtime_clone.environment)?.to_string(), - host_unnest.port_names, - host_unnest.port_numbers, // 15 - game_guard_unnest.port_names, - game_guard_unnest.port_numbers, - game_guard_unnest.gg_ports, - game_guard_unnest.protocols, - ) - .await - } - .boxed() - }) - .await?; + server_id, + game_id, + datacenter_id, + cluster_id, + serde_json::value::to_raw_value(&ctx.metadata.to_owned())?.to_string(), // 5 + resources.cpu_millicores, + resources.memory_mib, + ctx.kill_timeout_ms, + create_ts, + unwrap!(ctx.image_id).as_uuid(), // 10 + &ctx.args, + ctx.network_mode, + serde_json::value::to_raw_value(&ctx.environment)?.to_string(), + host_unnest.port_names, + host_unnest.port_numbers, // 15 + game_guard_unnest.port_names, + game_guard_unnest.port_numbers, + game_guard_unnest.gg_ports, + game_guard_unnest.protocols, + ) + .await } - } + .boxed() + }) + .await?; // let ( // (mm_game_config, namespace), @@ -325,16 +306,9 @@ pub async fn handle( let ctx: OperationContext = ctx; - let request_runtime = match unwrap!(ctx.runtime.clone()) { - dynamic_servers::server_create::request::Runtime::DockerRuntime(docker_runtime) => { - docker_runtime - } - }; - let request_runtime_network = unwrap!(request_runtime.network.clone()); - // Generate the Docker job - // let runtime = backend::dynamic_servers::lobby_runtime::Docker { + // let runtime = backend::ds::lobby_runtime::Docker { // build_id: todo!(), // args: docker_runtime.args, // env_vars: todo!(), @@ -395,7 +369,7 @@ pub async fn handle( // panic!(); // Validate build exists and belongs to this game - let build_id = unwrap_ref!(request_runtime.image_id).as_uuid(); + let build_id = unwrap_ref!(ctx.image_id).as_uuid(); let build_get = op!([ctx] build_get { build_ids: vec![build_id.into()], }) @@ -471,25 +445,25 @@ pub async fn handle( // // let network_mode = unwrap!(LobbyRuntimeNetworkMode::from_i32(runtime.network_mode)); // Read ports - let decoded_ports = request_runtime_network - .ports + let decoded_ports = ctx + .network_ports .clone() .into_iter() - .map(|(port, docker_port)| match docker_port.routing { - Some(backend::dynamic_servers::docker_port::Routing::GameGuard(game_guard_routing)) => { - let target = unwrap!(docker_port.port) as u16; + .map(|(port_label, port)| match port.routing { + Some(dynamic_servers::server_create::port::Routing::GameGuard(game_guard_routing)) => { + let target = unwrap!(port.server_port) as u16; GlobalResult::Ok(DecodedPort { - label: port.clone(), - nomad_port_label: util_mm::format_nomad_port_label(&port), + label: port_label.clone(), + nomad_port_label: util_ds::format_nomad_port_label(&port_label), target, - proxy_protocol: unwrap!(backend::dynamic_servers::GameGuardProtocol::from_i32( + proxy_protocol: unwrap!(backend::ds::GameGuardProtocol::from_i32( game_guard_routing.protocol )) .into(), }) } - Some(backend::dynamic_servers::docker_port::Routing::Host(_)) => { + Some(dynamic_servers::server_create::port::Routing::Host(_)) => { todo!() } None => { @@ -521,32 +495,23 @@ pub async fn handle( }) .collect::>(); - let prepared_ports = request_runtime_network - .ports - .iter() - .map(|(label, docker_port)| { - let mode = unwrap!(backend::dynamic_servers::DockerNetworkMode::from_i32( - request_runtime_network.mode - )); - let port_value = match mode { - // CNI will handle mapping the host port to the container port - backend::dynamic_servers::DockerNetworkMode::Bridge => { - unwrap!(docker_port.port).to_string() - } - // The container needs to listen on the correct port - backend::dynamic_servers::DockerNetworkMode::Host => { - template_env_var(&nomad_host_port_env_var(&label)) - } - }; - - GlobalResult::Ok(Some(String::new())) - // TODO - // Port with the kebab case port key. Included for backward compatabiilty & for - // less confusion. - // Ok((format!("PORT_{}", port.label.replace('-', "_")), port_value)) - }); - - // Also see util_mm:consts::DEFAULT_ENV_KEYS + let prepared_ports = ctx.network_ports.iter().map(|(label, port)| { + let mode = unwrap!(backend::ds::NetworkMode::from_i32(ctx.network_mode)); + let port_value = match mode { + // CNI will handle mapping the host port to the container port + backend::ds::NetworkMode::Bridge => unwrap!(port.server_port).to_string(), + // The container needs to listen on the correct port + backend::ds::NetworkMode::Host => template_env_var(&nomad_host_port_env_var(&label)), + }; + + GlobalResult::Ok(Some(String::new())) + // TODO + // Port with the kebab case port key. Included for backward compatabiilty & for + // less confusion. + // Ok((format!("PORT_{}", port.label.replace('-', "_")), port_value)) + }); + + // Also see util_ds:consts::DEFAULT_ENV_KEYS let mut env = Vec::<(String, String)>::new() .into_iter() // TODO @@ -705,13 +670,10 @@ pub async fn handle( Template { embedded_tmpl: Some(include_str!("./scripts/setup.sh").replace( "__HOST_NETWORK__", - match unwrap!( - backend::dynamic_servers::DockerNetworkMode::from_i32( - request_runtime_network.mode - ) - ) { - backend::dynamic_servers::DockerNetworkMode::Bridge => "false", - backend::dynamic_servers::DockerNetworkMode::Host => "true", + match unwrap!(backend::ds::NetworkMode::from_i32(ctx.network_mode)) + { + backend::ds::NetworkMode::Bridge => "false", + backend::ds::NetworkMode::Host => "true", }, )), dest_path: Some("${NOMAD_TASK_DIR}/setup.sh".into()), @@ -770,8 +732,8 @@ pub async fn handle( }, ]), resources: Some(Box::new(Resources { - CPU: Some(util_mm::RUNC_SETUP_CPU), - memory_mb: Some(util_mm::RUNC_SETUP_MEMORY), + CPU: Some(util_ds::RUNC_SETUP_CPU), + memory_mb: Some(util_ds::RUNC_SETUP_MEMORY), ..Resources::new() })), log_config: Some(Box::new(LogConfig { @@ -845,24 +807,24 @@ pub async fn handle( // .ports // .iter() // .filter(|port| { - // port.proxy_kind == backend::dynamic_servers::lobby_runtime::ProxyKind::GameGuard as i32 + // port.proxy_kind == backend::ds::lobby_runtime::ProxyKind::GameGuard as i32 // && port.port_range.is_none() // }) // .flat_map(|port| { // let mut ports = vec![direct_proxied_port(lobby_id, region_id, port)]; - // match backend::dynamic_servers::lobby_runtime::ProxyProtocol::from_i32( + // match backend::ds::lobby_runtime::ProxyProtocol::from_i32( // port.proxy_protocol, // ) { // Some( - // backend::dynamic_servers::lobby_runtime::ProxyProtocol::Http - // | backend::dynamic_servers::lobby_runtime::ProxyProtocol::Https, + // backend::ds::lobby_runtime::ProxyProtocol::Http + // | backend::ds::lobby_runtime::ProxyProtocol::Https, // ) => { // ports.push(path_proxied_port(lobby_id, region_id, port)); // } // Some( - // backend::dynamic_servers::lobby_runtime::ProxyProtocol::Udp - // | backend::dynamic_servers::lobby_runtime::ProxyProtocol::Tcp - // | backend::dynamic_servers::lobby_runtime::ProxyProtocol::TcpTls, + // backend::ds::lobby_runtime::ProxyProtocol::Udp + // | backend::ds::lobby_runtime::ProxyProtocol::Tcp + // | backend::ds::lobby_runtime::ProxyProtocol::TcpTls, // ) // | None => {} // } @@ -1295,8 +1257,7 @@ pub async fn handle( key: "user_env".into(), // other locations value: unwrap!(serde_json::to_string( - &request_runtime - .environment + &ctx.environment .iter() .map(|(k, v)| (k.clone(), escape_go_template(v))) .collect::>(), @@ -1385,32 +1346,49 @@ pub async fn handle( // }) // .await?; + // Build response ports + let network_ports = ctx + .network_ports + .iter() + .map(|(port_label, port)| { + GlobalResult::Ok(( + port_label.clone(), + backend::ds::Port { + server_port: port.server_port, + public_hostname: None, + public_port: None, + routing: Some(match unwrap!(port.routing.clone()) { + dynamic_servers::server_create::port::Routing::GameGuard(x) => { + backend::ds::port::Routing::GameGuard(x) + } + dynamic_servers::server_create::port::Routing::Host(x) => { + backend::ds::port::Routing::Host(x) + } + }), + }, + )) + }) + .collect::>>()?; + Ok(dynamic_servers::server_create::Response { - server: Some(backend::dynamic_servers::Server { + server: Some(backend::ds::Server { server_id: Some(server_id.into()), game_id: Some(game_id.into()), datacenter_id: Some(datacenter_id.into()), cluster_id: Some(cluster_id.into()), metadata: ctx.metadata.clone(), - resources: Some(backend::dynamic_servers::ServerResources { + resources: Some(backend::ds::ServerResources { cpu_millicores: resources.cpu_millicores, memory_mib: resources.memory_mib, }), kill_timeout_ms: ctx.kill_timeout_ms, create_ts, destroy_ts: None, - runtime: Some(match runtime { - dynamic_servers::server_create::request::Runtime::DockerRuntime(docker_runtime) => { - backend::dynamic_servers::server::Runtime::DockerRuntime( - backend::dynamic_servers::DockerRuntime { - args: docker_runtime.args, - environment: docker_runtime.environment, - image_id: docker_runtime.image_id, - network: docker_runtime.network, - }, - ) - } - }), + args: ctx.args.clone(), + environment: ctx.environment.clone(), + image_id: ctx.image_id, + network_mode: ctx.network_mode, + network_ports, }), }) } diff --git a/svc/pkg/ds/ops/server-create/src/nomad_job.rs b/svc/pkg/ds/ops/server-create/src/nomad_job.rs index 26d187d84..bf683aaa2 100644 --- a/svc/pkg/ds/ops/server-create/src/nomad_job.rs +++ b/svc/pkg/ds/ops/server-create/src/nomad_job.rs @@ -43,14 +43,14 @@ pub enum ProxyProtocol { Udp, } -impl From for ProxyProtocol { - fn from(protocol: backend::dynamic_servers::GameGuardProtocol) -> Self { +impl From for ProxyProtocol { + fn from(protocol: backend::ds::GameGuardProtocol) -> Self { match protocol { - backend::dynamic_servers::GameGuardProtocol::Http => Self::Http, - backend::dynamic_servers::GameGuardProtocol::Https => Self::Https, - backend::dynamic_servers::GameGuardProtocol::Tcp => Self::Tcp, - backend::dynamic_servers::GameGuardProtocol::TcpTls => Self::TcpTls, - backend::dynamic_servers::GameGuardProtocol::Udp => Self::Udp, + backend::ds::GameGuardProtocol::Http => Self::Http, + backend::ds::GameGuardProtocol::Https => Self::Https, + backend::ds::GameGuardProtocol::Tcp => Self::Tcp, + backend::ds::GameGuardProtocol::TcpTls => Self::TcpTls, + backend::ds::GameGuardProtocol::Udp => Self::Udp, } } } diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/consts.rs b/svc/pkg/ds/ops/server-create/src/util_mm/consts.rs deleted file mode 100644 index 10d67ca4a..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/consts.rs +++ /dev/null @@ -1,39 +0,0 @@ -use rivet_util as util; - -pub const LOBBY_READY_TIMEOUT: i64 = util::duration::minutes(5); -pub const PLAYER_READY_TIMEOUT: i64 = util::duration::minutes(2); -pub const PLAYER_AUTO_REMOVE_TIMEOUT: i64 = util::duration::hours(8); - -pub const MIN_HOST_PORT: u16 = 26000; -pub const MAX_HOST_PORT: u16 = 31999; - -/// Constants used for mocking responses when using dev tokens. -pub const DEV_REGION_ID: &str = "dev-lcl"; -pub const DEV_PROVIDER_NAME: &str = "Development"; -pub const DEV_REGION_NAME: &str = "Local"; - -// Also see svc/mm-lobby-create/src/nomad_job.rs -pub const DEFAULT_ENV_KEYS: &[&str] = &[ - "RIVET_API_ENDPOINT", - "RIVET_CHAT_API_URL", - "RIVET_GROUP_API_URL", - "RIVET_IDENTITY_API_URL", - "RIVET_KV_API_URL", - "RIVET_MATCHMAKER_API_URL", - "RIVET_NAMESPACE_NAME", - "RIVET_NAMESPACE_ID", - "RIVET_VERSION_NAME", - "RIVET_VERSION_ID", - "RIVET_GAME_MODE_ID", - "RIVET_GAME_MODE_NAME", - "RIVET_LOBBY_ID", - "RIVET_TOKEN", - "RIVET_REGION_ID", - "RIVET_REGION_NAME", - "RIVET_MAX_PLAYERS_NORMAL", - "RIVET_MAX_PLAYERS_DIRECT", - "RIVET_MAX_PLAYERS_PARTY", - "RIVET_LOBBY_TOKEN", - "RIVET_LOBBY_GROUP_ID", - "RIVET_LOBBY_GROUP_NAME", -]; diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/defaults.rs b/svc/pkg/ds/ops/server-create/src/util_mm/defaults.rs deleted file mode 100644 index 1c85d6df0..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/defaults.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub const TIER_NAME_ID: &str = "basic-1d1"; -pub const MAX_PLAYERS_NORMAL: u32 = 32; -pub const MAX_PLAYERS_DIRECT: u32 = 40; -pub const MAX_PLAYERS_PARTY: u32 = 40; diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/key.rs b/svc/pkg/ds/ops/server-create/src/util_mm/key.rs deleted file mode 100644 index d62c244ea..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/key.rs +++ /dev/null @@ -1,207 +0,0 @@ -use uuid::Uuid; - -/// HASH -pub fn player_config(player_id: Uuid) -> String { - format!("{{global}}:mm:player:{}:config", player_id) -} - -pub mod player_config { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct Config { - #[serde(rename = "l")] - pub lobby_id: Uuid, - #[serde(rename = "qi")] - pub query_id: Option, - #[serde(rename = "ra")] - pub remote_address: String, - } - - pub const LOBBY_ID: &str = "l"; - pub const QUERY_ID: &str = "qi"; - pub const REMOTE_ADDRESS: &str = "ra"; -} - -/// HASH -pub fn lobby_config(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:config", lobby_id) -} - -/// HASH -pub fn lobby_tags(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:tags", lobby_id) -} - -pub mod lobby_config { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct Config { - #[serde(rename = "ns")] - pub namespace_id: Uuid, - #[serde(rename = "r")] - pub region_id: Uuid, - #[serde(rename = "lg")] - pub lobby_group_id: Uuid, - #[serde(rename = "mpn")] - pub max_players_normal: u32, - #[serde(rename = "mpp")] - pub max_players_party: u32, - #[serde(rename = "mpd")] - pub max_players_direct: u32, - #[serde(rename = "p")] - pub preemptive: bool, - #[serde(rename = "rt", skip_serializing_if = "Option::is_none")] - pub ready_ts: Option, - #[serde(rename = "c")] - pub is_closed: bool, - #[serde(rename = "cu")] - pub is_custom: bool, - #[serde(rename = "st", skip_serializing_if = "Option::is_none")] - pub state_json: Option, - } - - pub const NAMESPACE_ID: &str = "ns"; - pub const REGION_ID: &str = "r"; - pub const LOBBY_GROUP_ID: &str = "lg"; - pub const MAX_PLAYERS_NORMAL: &str = "mpn"; - pub const MAX_PLAYERS_PARTY: &str = "mpp"; - pub const MAX_PLAYERS_DIRECT: &str = "mpd"; - pub const PREEMPTIVE: &str = "p"; - pub const READY_TS: &str = "rt"; - pub const IS_CLOSED: &str = "c"; - pub const IS_CUSTOM: &str = "cu"; - pub const STATE_JSON: &str = "st"; -} - -/// HASH -/// -/// Includes the state of all active find queries. -pub fn find_query_state(query_id: Uuid) -> String { - format!("{{global}}:mm:find_query:{}:state", query_id) -} - -pub mod find_query_state { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct State { - #[serde(rename = "n")] - pub namespace_id: Uuid, - #[serde(rename = "l", skip_serializing_if = "Option::is_none")] - pub lobby_id: Option, - #[serde(rename = "lac", skip_serializing_if = "Option::is_none")] - pub lobby_auto_created: Option, - #[serde(rename = "s")] - pub status: u8, - } - - pub const NAMESPACE_ID: &str = "n"; - pub const PLAYER_IDS: &str = "pl"; - pub const LOBBY_ID: &str = "l"; - pub const LOBBY_AUTO_CREATED: &str = "lac"; - pub const STATUS: &str = "s"; -} - -/// SET -pub fn find_query_player_ids(query_id: Uuid) -> String { - format!("{{global}}:mm:find_query:{}:player_ids", query_id) -} - -/// ZSET -/// -/// Includes all active find queries for a lobby. -pub fn lobby_find_queries(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:find_queries", lobby_id) -} - -/// ZSET -pub fn ns_player_ids(namespace_id: Uuid) -> String { - format!("{{global}}:mm:ns:{}:player_ids", namespace_id) -} - -/// ZSET -pub fn ns_lobby_ids(namespace_id: Uuid) -> String { - format!("{{global}}:mm:ns:{}:lobby_ids", namespace_id) -} - -/// SET -pub fn ns_remote_address_player_ids(namespace_id: Uuid, remote_address: &str) -> String { - format!( - "{{global}}:mm:ns:{}:remote_address:{}:player_ids", - namespace_id, remote_address - ) -} - -/// ZSET -pub fn lobby_player_ids(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:player_ids", lobby_id) -} - -/// ZSET -pub fn lobby_registered_player_ids(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:registered_player_ids", lobby_id) -} - -/// ZSET -pub fn idle_lobby_ids(namespace_id: Uuid, region_id: Uuid, lobby_group_id: Uuid) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lg:{}:idle_lobby_ids", - namespace_id, region_id, lobby_group_id - ) -} - -/// Map containing all idle lobbies and their associated lobby group -/// IDs. -/// -/// We limit this to just idle lobbies since we need to iterate over all -/// the values in this hash in mm-lobby-idle-update, so we want to limit -/// the values in here as much as possible. -/// -/// We keep this all in one hash so we only have to lock one key instead -/// of using `SCAN`. -/// -/// HASH -pub fn idle_lobby_lobby_group_ids(namespace_id: Uuid, region_id: Uuid) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lobby:idle:lobby_group_ids", - namespace_id, region_id, - ) -} - -/// ZSET -pub fn lobby_available_spots( - namespace_id: Uuid, - region_id: Uuid, - lobby_group_id: Uuid, - join_kind: super::JoinKind, -) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lg:{}:lobby:available_spots:{}", - namespace_id, - region_id, - lobby_group_id, - join_kind.short() - ) -} - -/// ZSET -pub fn lobby_unready() -> String { - "{global}:mm:lobby:unready".to_string() -} - -/// ZSET -pub fn player_unregistered() -> String { - "{global}:mm:player:unregistered".to_string() -} - -/// ZSET -pub fn player_auto_remove() -> String { - "{global}:mm:player:auto_remove".to_string() -} - -// Placeholder key -pub fn empty() -> String { - "{global}".to_string() -} diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/mod.rs b/svc/pkg/ds/ops/server-create/src/util_mm/mod.rs deleted file mode 100644 index c0c905167..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -pub mod consts; -pub mod defaults; -pub mod key; -pub mod test; -pub mod verification; -pub mod version_migrations; - -pub enum JoinKind { - Normal, - Party, - Direct, -} - -impl JoinKind { - pub fn short(self) -> &'static str { - match self { - JoinKind::Normal => "normal", - JoinKind::Party => "party", - JoinKind::Direct => "direct", - } - } -} - -#[derive(Debug, PartialEq, strum::FromRepr)] -#[repr(u8)] -pub enum FindQueryStatus { - /// Lobby is creating or in between mm-lobby-find and - /// mm-lobby-find-try-complete. - Pending = 0, - /// Find finished and lobby is ready. - Complete = 1, - /// There was an error. - Fail = 2, -} - -/// Formats the port label to be used in Nomad. -/// -/// Prefixing this port ensure that the user defined port names don't interfere -/// with other ports. -pub fn format_nomad_port_label(port_label: &str) -> String { - let snake_port_label = heck::SnakeCase::to_snake_case(port_label); - format!("ds_{snake_port_label}") -} - -pub const RUNC_SETUP_CPU: i32 = 50; -pub const RUNC_SETUP_MEMORY: i32 = 32; -pub const RUNC_CLEANUP_CPU: i32 = 50; -pub const RUNC_CLEANUP_MEMORY: i32 = 32; diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/test.rs b/svc/pkg/ds/ops/server-create/src/util_mm/test.rs deleted file mode 100644 index 8de746645..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/test.rs +++ /dev/null @@ -1 +0,0 @@ -pub const TIER_NAME_ID: &str = "basic-1d16"; diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/verification.rs b/svc/pkg/ds/ops/server-create/src/util_mm/verification.rs deleted file mode 100644 index ae51d87f5..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/verification.rs +++ /dev/null @@ -1,391 +0,0 @@ -use std::collections::HashMap; - -use futures_util::{StreamExt, TryStreamExt}; -use http::StatusCode; -use proto::backend::{self, pkg::*}; -use rivet_operation::prelude::*; -use serde::Serialize; -use uuid::Uuid; - -#[derive(Serialize)] -pub struct ExternalVerificationRequest { - pub verification_data: Option, - pub game: Game, - pub clients: HashMap, - pub join_kind: JoinKind, - pub kind: ConnectionKind, -} - -#[derive(Serialize)] -pub struct Game { - pub namespace_id: Uuid, - pub game_mode_id: Uuid, - pub game_mode_name_id: String, - - pub lobby: Option, - pub state: Option, - pub config: Option, - pub tags: HashMap, - pub dynamic_max_players: Option, -} - -#[derive(Serialize)] -pub struct Lobby { - pub lobby_id: Uuid, - pub region_id: Uuid, - pub region_name_id: String, - pub create_ts: String, - pub is_closed: bool, -} - -#[derive(Serialize)] -pub struct Client { - pub user_agent: Option, - pub latitude: Option, - pub longitude: Option, -} - -#[derive(Serialize)] -pub enum JoinKind { - Normal, - Party, -} - -#[derive(Copy, Clone, Serialize)] -pub enum ConnectionKind { - Find, - Join, - Create, -} - -pub struct VerifyConfigOpts<'a> { - pub kind: ConnectionKind, - pub namespace_id: Uuid, - pub user_id: Option, - pub client_info: Vec, - pub tags: &'a HashMap, - pub dynamic_max_players: Option, - - pub lobby_groups: &'a [backend::matchmaker::LobbyGroup], - pub lobby_group_meta: &'a [backend::matchmaker::LobbyGroupMeta], - pub lobby_info: Option<&'a backend::matchmaker::Lobby>, - pub lobby_state_json: Option<&'a str>, - - pub verification_data_json: Option<&'a str>, - pub lobby_config_json: Option<&'a str>, - pub custom_lobby_publicity: Option, -} - -struct ExternalRequestConfigAndLobby<'a> { - pub lobby_group: &'a backend::matchmaker::LobbyGroup, - pub lobby_group_meta: &'a backend::matchmaker::LobbyGroupMeta, - external_request_config: backend::net::ExternalRequestConfig, -} - -/// Verifies everything required to make a find request or create a custom lobby. -pub async fn verify_config( - ctx: &OperationContext<()>, - opts: &VerifyConfigOpts<'_>, -) -> GlobalResult<()> { - let mut highest_identity_requirement = backend::matchmaker::IdentityRequirement::None; - let mut external_request_configs = Vec::new(); - - // Collect all external request configs and identity requirement - for (lobby_group, lobby_group_meta) in opts.lobby_groups.iter().zip(opts.lobby_group_meta) { - let (identity_requirement, external_request_config) = match ( - opts.kind, - lobby_group.actions.as_ref().and_then(|a| a.find.as_ref()), - lobby_group.actions.as_ref().and_then(|a| a.join.as_ref()), - lobby_group.actions.as_ref().and_then(|a| a.create.as_ref()), - ) { - (ConnectionKind::Find, Some(find_config), _, _) => { - if !find_config.enabled { - bail_with!(MATCHMAKER_FIND_DISABLED); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - find_config.identity_requirement - ), - "invalid identity requirement variant" - ), - find_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Join, _, Some(join_config), _) => { - if !join_config.enabled { - bail_with!(MATCHMAKER_JOIN_DISABLED); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - join_config.identity_requirement - ), - "invalid identity requirement variant" - ), - join_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Create, _, _, Some(create_config)) => { - let publicity = unwrap!(opts.custom_lobby_publicity); - - // Verify publicity - match ( - publicity, - create_config.enable_public, - create_config.enable_private, - ) { - (backend::matchmaker::lobby::Publicity::Public, allowed, _) => { - ensure_with!( - allowed, - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = r#""public" publicity not allowed with this custom game mode"# - ); - } - (backend::matchmaker::lobby::Publicity::Private, _, allowed) => { - ensure_with!( - allowed, - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = - r#""private" publicity not allowed with this custom game mode"# - ); - } - } - - // Verify lobby count - if let (Some(max_lobbies_per_identity), Some(user_id)) = - (create_config.max_lobbies_per_identity, opts.user_id) - { - let lobbies_res = op!([ctx] mm_lobby_list_for_user_id { - user_ids: vec![user_id.into()], - }) - .await?; - let user = unwrap!(lobbies_res.users.first()); - ensure_with!( - (user.lobby_ids.len() as u64) < max_lobbies_per_identity, - MATCHMAKER_CUSTOM_LOBBY_LIMIT_REACHED - ); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - create_config.identity_requirement - ), - "invalid identity requirement variant" - ), - create_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Create, _, _, None) => { - bail_with!(MATCHMAKER_CUSTOM_LOBBIES_DISABLED); - } - _ => (backend::matchmaker::IdentityRequirement::None, None), - }; - - // Updated highest requirement - match highest_identity_requirement { - backend::matchmaker::IdentityRequirement::None => { - highest_identity_requirement = identity_requirement; - } - backend::matchmaker::IdentityRequirement::Guest => { - if matches!( - identity_requirement, - backend::matchmaker::IdentityRequirement::Registered - ) { - highest_identity_requirement = identity_requirement; - } - } - backend::matchmaker::IdentityRequirement::Registered => {} - } - - if let Some(external_request_config) = external_request_config { - external_request_configs.push(ExternalRequestConfigAndLobby { - lobby_group, - lobby_group_meta, - external_request_config, - }); - } - } - - // Verify identity requirement - match (highest_identity_requirement, opts.user_id) { - (backend::matchmaker::IdentityRequirement::Registered, Some(user_id)) => { - let user_identities_res = op!([ctx] user_identity_get { - user_ids: vec![user_id.into()], - }) - .await?; - let user = unwrap!( - user_identities_res.users.first(), - "could not find user identities" - ); - let is_registered = !user.identities.is_empty(); - - if !is_registered { - bail_with!(MATCHMAKER_REGISTRATION_REQUIRED); - } - } - ( - backend::matchmaker::IdentityRequirement::Guest - | backend::matchmaker::IdentityRequirement::Registered, - None, - ) => { - bail_with!(MATCHMAKER_IDENTITY_REQUIRED); - } - _ => {} - } - - // Verify lobby config - if let Some(lobby_config_json) = opts.lobby_config_json { - ensure_with!( - lobby_config_json.len() as u64 <= util::file_size::kibibytes(16), - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = "too large (> 16KiB)" - ); - } - - // Verify user data externally - for external_request_config_and_lobby in external_request_configs { - let ExternalRequestConfigAndLobby { - lobby_group, - lobby_group_meta, - external_request_config, - } = external_request_config_and_lobby; - - // Build lobby info - let lobby = if let Some(l) = &opts.lobby_info { - // Fetch region data for readable name - let region_id = unwrap!(l.region_id); - let regions_res = op!([ctx] region_get { - region_ids: vec![region_id], - }) - .await?; - let region = unwrap!(regions_res.regions.first()); - - Some(Lobby { - lobby_id: unwrap_ref!(l.lobby_id).as_uuid(), - region_id: region_id.as_uuid(), - region_name_id: region.name_id.clone(), - create_ts: util::timestamp::to_string(l.create_ts)?, - is_closed: l.is_closed, - }) - } else { - None - }; - - // Fetch IP info - let clients = futures_util::stream::iter( - opts.client_info - .iter() - .filter_map(|client_info| { - client_info - .remote_address - .as_ref() - .map(|ip| (ip.clone(), client_info.user_agent.clone())) - }) - .collect::>(), - ) - .map(|(ip, user_agent)| async move { - let ip_res = op!([ctx] ip_info { - ip: ip.clone(), - }) - .await?; - let (latitude, longitude) = ip_res - .ip_info - .and_then(|ip_info| ip_info.coords) - .map(|coords| (coords.latitude, coords.longitude)) - .unzip(); - - GlobalResult::Ok(( - ip.clone(), - Client { - user_agent: user_agent.clone(), - longitude, - latitude, - }, - )) - }) - .buffer_unordered(16) - .try_collect::>() - .await?; - - // Build body - let body = ExternalVerificationRequest { - verification_data: opts - .verification_data_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - game: Game { - game_mode_id: unwrap_ref!(lobby_group_meta.lobby_group_id).as_uuid(), - game_mode_name_id: lobby_group.name_id.clone(), - namespace_id: opts.namespace_id, - - lobby, - state: opts - .lobby_state_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - config: opts - .lobby_config_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - tags: opts.tags.clone(), - dynamic_max_players: opts.dynamic_max_players, - }, - clients, - join_kind: JoinKind::Normal, - kind: opts.kind, - }; - - // Send request - let request_id = Uuid::new_v4(); - let external_res = msg!([ctx] external::msg::request_call(request_id) - -> Result - { - request_id: Some(request_id.into()), - config: Some(external_request_config), - timeout: util::duration::seconds(10) as u64, - body: Some(serde_json::to_vec(&body)?), - ..Default::default() - }) - .await?; - - // Handle status code - if let Ok(res) = external_res { - let status = StatusCode::from_u16(res.status_code as u16)?; - - tracing::info!(?status, "user verification response"); - - if !status.is_success() { - bail_with!(MATCHMAKER_VERIFICATION_FAILED); - } - } else { - bail_with!(MATCHMAKER_VERIFICATION_REQUEST_FAILED); - } - } - - Ok(()) -} diff --git a/svc/pkg/ds/ops/server-create/src/util_mm/version_migrations.rs b/svc/pkg/ds/ops/server-create/src/util_mm/version_migrations.rs deleted file mode 100644 index ed2064c68..000000000 --- a/svc/pkg/ds/ops/server-create/src/util_mm/version_migrations.rs +++ /dev/null @@ -1,8 +0,0 @@ -use bit_vec::BitVec; - -pub const PORT_RANGE_PROXY_IDX: usize = 0; - -/// The bit flags expected for a game version with all migrations applied. -pub fn all() -> BitVec { - BitVec::from_elem(1, true) -} diff --git a/svc/pkg/ds/ops/server-create/tests/integration.rs b/svc/pkg/ds/ops/server-create/tests/integration.rs index c4dfc9326..ad65fde41 100644 --- a/svc/pkg/ds/ops/server-create/tests/integration.rs +++ b/svc/pkg/ds/ops/server-create/tests/integration.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use chirp_worker::prelude::*; -use proto::backend; +use proto::backend::{self, pkg::dynamic_servers}; #[worker_test] async fn create(ctx: TestCtx) { @@ -42,53 +42,43 @@ async fn create(ctx: TestCtx) { .await .unwrap(); - let runtime = Some( - proto::backend::pkg::dynamic_servers::server_create::request::Runtime::DockerRuntime( - proto::backend::dynamic_servers::DockerRuntime { - args: Vec::new(), - environment: vec![ - ("some_envkey_test".to_string(), "2134523".to_string()), - ( - "some_other_envkey_test".to_string(), - "4325234356".to_string(), - ), - ] - .into_iter() - .collect(), - image_id: Some(build_res.build_id.unwrap()), - network: Some(proto::backend::dynamic_servers::DockerNetwork { - mode: 0, - ports: vec![( - "testing2".to_string(), - backend::dynamic_servers::DockerPort { - port: Some(28234), - routing: Some( - backend::dynamic_servers::docker_port::Routing::GameGuard( - backend::dynamic_servers::DockerGameGuardRouting { - protocol: 0, - }, - ), - ), - }, - )] - // Collect into hashmap - .into_iter() - .collect(), - }), - }, + let faker_region = op!([ctx] faker_region {}).await.unwrap(); + + let env = vec![ + ("some_envkey_test".to_string(), "2134523".to_string()), + ( + "some_other_envkey_test".to_string(), + "4325234356".to_string(), ), - ); + ] + .into_iter() + .collect(); - let faker_region = op!([ctx] faker_region {}).await.unwrap(); + let ports = vec![( + "testing2".to_string(), + dynamic_servers::server_create::Port { + server_port: Some(28234), + routing: Some(dynamic_servers::server_create::port::Routing::GameGuard( + backend::ds::GameGuardRouting { protocol: 0 }, + )), + }, + )] + // Collect into hashmap + .into_iter() + .collect(); let server = op!([ctx] ds_server_create { game_id: Some(game_id), cluster_id: Some(cluster_id), datacenter_id: faker_region.region_id, - resources: Some(proto::backend::dynamic_servers::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), kill_timeout_ms: 0, metadata: HashMap::new(), - runtime: runtime, + args: Vec::new(), + environment: env, + image_id: Some(build_res.build_id.unwrap()), + network_mode: 0, + network_ports: ports, }) .await .unwrap() diff --git a/svc/pkg/ds/ops/server-create/tests/print_test_data.rs b/svc/pkg/ds/ops/server-create/tests/print_test_data.rs index 6e13fe5d5..e6c07ddaa 100644 --- a/svc/pkg/ds/ops/server-create/tests/print_test_data.rs +++ b/svc/pkg/ds/ops/server-create/tests/print_test_data.rs @@ -65,19 +65,19 @@ async fn print_test_data(ctx: TestCtx) { // // let runtime = Some( // proto::backend::pkg::dynamic_servers::server_create::request::Runtime::DockerRuntime( - // proto::backend::dynamic_servers::DockerRuntime { + // proto::backend::ds::DockerRuntime { // args: Vec::new(), // environment: HashMap::new(), // image_id: Some(build_res.build_id.unwrap()), - // network: Some(proto::backend::dynamic_servers::DockerNetwork { + // network: Some(proto::backend::ds::DockerNetwork { // mode: 0, // ports: vec![( // "testing2".to_string(), - // backend::dynamic_servers::DockerPort { + // backend::ds::DockerPort { // port: Some(28234), // routing: Some( - // backend::dynamic_servers::docker_port::Routing::GameGuard( - // backend::dynamic_servers::DockerGameGuardRouting { + // backend::ds::docker_port::Routing::GameGuard( + // backend::ds::DockerGameGuardRouting { // protocol: 0, // }, // ), @@ -100,7 +100,7 @@ async fn print_test_data(ctx: TestCtx) { // game_id: Some(game_id), // cluster_id: Some(cluster_id), // datacenter_id: faker_region.region_id, - // resources: Some(proto::backend::dynamic_servers::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + // resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), // kill_timeout_ms: 0, // metadata: HashMap::new(), // runtime: runtime, diff --git a/svc/pkg/ds/ops/server-delete/src/lib.rs b/svc/pkg/ds/ops/server-delete/src/lib.rs index 4cead8231..0a8175680 100644 --- a/svc/pkg/ds/ops/server-delete/src/lib.rs +++ b/svc/pkg/ds/ops/server-delete/src/lib.rs @@ -1,9 +1,7 @@ use chirp_worker::prelude::*; use futures_util::FutureExt; use proto::backend::pkg::*; -use rivet_operation::prelude::*; use tokio::task; -// use crate::sqlx; #[derive(Debug, sqlx::FromRow)] struct UpdatedServer { @@ -274,74 +272,73 @@ pub async fn handle( Ok(_) => { tracing::info!("job stopped"); - task::spawn(async move { - // tokio::time::sleep(util_job::JOB_STOP_TIMEOUT).await; - - // tracing::info!(?dynamic_server.alloc_id, "manually killing allocation"); - - // if let Err(err) = { - // let local_var_client = &configuration.client; - - // let local_var_uri_str = format!( - // "{}/client/allocation/{alloc_id}/signal", - // configuration.base_path, - // alloc_id = nomad_client::apis::urlencode(dynamic_server.alloc_id), - // ); - // let mut local_var_req_builder = - // local_var_client.post(local_var_uri_str.as_str()); - - // if let Some(ref local_var_str) = namespace { - // local_var_req_builder = local_var_req_builder - // .query(&[("namespace", &local_var_str.to_string())]); - // } - // if let Some(ref local_var_str) = region { - // local_var_req_builder = local_var_req_builder - // .query(&[("region", &local_var_str.to_string())]); - // } - // if let Some(ref local_var_str) = index { - // local_var_req_builder = local_var_req_builder - // .query(&[("index", &local_var_str.to_string())]); - // } - // if let Some(ref local_var_str) = wait { - // local_var_req_builder = local_var_req_builder - // .query(&[("wait", &local_var_str.to_string())]); - // } - // if let Some(ref local_var_user_agent) = configuration.user_agent { - // local_var_req_builder = local_var_req_builder - // .header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); - // } - // local_var_req_builder = local_var_req_builder.json(&alloc_signal_request); - - // let local_var_req = local_var_req_builder.build()?; - // let local_var_resp = local_var_client.execute(local_var_req).await?; - - // let local_var_status = local_var_resp.status(); - // let local_var_content = local_var_resp.text().await?; - - // if !local_var_status.is_client_error() - // && !local_var_status.is_server_error() - // { - // Ok(()) - // } else { - // let local_var_entity: Option< - // nomad_client::apis::allocations_api::SignalAllocationError, - // > = serde_json::from_str(&local_var_content).ok(); - // let local_var_error = nomad_client::apis::ResponseContent { - // status: local_var_status, - // content: local_var_content, - // entity: local_var_entity, - // }; - // Err(nomad_client::apis::Error::ResponseError(local_var_error)) - // } - // } { - // tracing::warn!( - // ?err, - // ?alloc_id, - // "error while trying to manually kill allocation" - // ); - // } - }); - + task::spawn(async move { + // tokio::time::sleep(util_job::JOB_STOP_TIMEOUT).await; + + // tracing::info!(?dynamic_server.alloc_id, "manually killing allocation"); + + // if let Err(err) = { + // let local_var_client = &configuration.client; + + // let local_var_uri_str = format!( + // "{}/client/allocation/{alloc_id}/signal", + // configuration.base_path, + // alloc_id = nomad_client::apis::urlencode(dynamic_server.alloc_id), + // ); + // let mut local_var_req_builder = + // local_var_client.post(local_var_uri_str.as_str()); + + // if let Some(ref local_var_str) = namespace { + // local_var_req_builder = local_var_req_builder + // .query(&[("namespace", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = region { + // local_var_req_builder = local_var_req_builder + // .query(&[("region", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = index { + // local_var_req_builder = local_var_req_builder + // .query(&[("index", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = wait { + // local_var_req_builder = local_var_req_builder + // .query(&[("wait", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_user_agent) = configuration.user_agent { + // local_var_req_builder = local_var_req_builder + // .header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + // } + // local_var_req_builder = local_var_req_builder.json(&alloc_signal_request); + + // let local_var_req = local_var_req_builder.build()?; + // let local_var_resp = local_var_client.execute(local_var_req).await?; + + // let local_var_status = local_var_resp.status(); + // let local_var_content = local_var_resp.text().await?; + + // if !local_var_status.is_client_error() + // && !local_var_status.is_server_error() + // { + // Ok(()) + // } else { + // let local_var_entity: Option< + // nomad_client::apis::allocations_api::SignalAllocationError, + // > = serde_json::from_str(&local_var_content).ok(); + // let local_var_error = nomad_client::apis::ResponseContent { + // status: local_var_status, + // content: local_var_content, + // entity: local_var_entity, + // }; + // Err(nomad_client::apis::Error::ResponseError(local_var_error)) + // } + // } { + // tracing::warn!( + // ?err, + // ?alloc_id, + // "error while trying to manually kill allocation" + // ); + // } + }); } Err(err) => { tracing::warn!(?err, "error thrown while stopping job, probably a 404, will continue as if stopped normally"); diff --git a/svc/pkg/ds/ops/server-get/Cargo.toml b/svc/pkg/ds/ops/server-get/Cargo.toml new file mode 100644 index 000000000..9d1e375df --- /dev/null +++ b/svc/pkg/ds/ops/server-get/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ds-server-get" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-client = { path = "../../../../../lib/chirp/client" } +rivet-operation = { path = "../../../../../lib/operation/core" } +util-ds = { package = "rivet-util-ds", path = "../../util" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } + +ds-server-create = { path = "../server-create" } +cluster-list = { path = "../../../cluster/ops/list" } +faker-build = { path = "../../../faker/ops/build" } +faker-game = { path = "../../../faker/ops/game" } +faker-region = { path = "../../../faker/ops/region" } + diff --git a/svc/pkg/ds/ops/server-get/Service.toml b/svc/pkg/ds/ops/server-get/Service.toml new file mode 100644 index 000000000..4ca136b6a --- /dev/null +++ b/svc/pkg/ds/ops/server-get/Service.toml @@ -0,0 +1,7 @@ +[service] +name = "ds-server-get" + +[runtime] +kind = "rust" + +[operation] diff --git a/svc/pkg/ds/ops/server-get/src/lib.rs b/svc/pkg/ds/ops/server-get/src/lib.rs new file mode 100644 index 000000000..8697c2566 --- /dev/null +++ b/svc/pkg/ds/ops/server-get/src/lib.rs @@ -0,0 +1,240 @@ +use futures_util::FutureExt; +use proto::backend::{self, pkg::*}; +use rivet_operation::prelude::*; +use std::collections::HashMap; + +#[derive(sqlx::FromRow)] +struct Server { + server_id: Uuid, + game_id: Uuid, + datacenter_id: Uuid, + cluster_id: Uuid, + metadata: serde_json::Value, + resources_cpu_millicores: i64, + resources_memory_mib: i64, + kill_timeout_ms: i64, + create_ts: i64, + destroy_ts: Option, + image_id: Uuid, + args: Vec, + network_mode: i64, + environment: serde_json::Value, +} + +#[derive(sqlx::FromRow)] +struct DockerPortProtocolGameGuard { + server_id: Uuid, + port_name: String, + port_number: i64, + gg_port: i64, + protocol: i64, +} + +#[derive(sqlx::FromRow)] +struct DockerPortHost { + server_id: Uuid, + port_name: String, + port_number: i64, + protocol: i64, +} + +#[derive(sqlx::FromRow)] +struct ServerNomad { + server_id: Uuid, + nomad_dispatched_job_id: Option, + nomad_alloc_id: Option, + nomad_node_id: Option, + nomad_node_name: Option, + nomad_node_public_ipv4: Option, + nomad_node_vlan_ipv4: Option, + nomad_alloc_plan_ts: Option, +} + +#[derive(sqlx::FromRow)] +struct ServerPort { + server_id: Uuid, + nomad_label: String, + nomad_ip: String, + nomad_source: i64, +} + +#[operation(name = "ds-server-get")] +pub async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let server_ids = ctx + .server_ids + .iter() + .map(common::Uuid::as_uuid) + .collect::>(); + + let (server_rows, port_gg_rows, port_host_rows, server_nomad_rows, server_port_rows) = tokio::try_join!( + sql_fetch_all!( + [ctx, Server] + " + SELECT server_id, game_id, datacenter_id, cluster_id, metadata, resources_cpu_millicores, resources_memory_mib, kill_timeout_ms, create_ts, destroy_ts, image_id, args, network_mode, environment + FROM db_dynamic_servers.servers + WHERE server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, DockerPortProtocolGameGuard] + " + SELECT server_id, port_name, port_number, gg_port, protocol + FROM db_dynamic_servers.docker_ports_protocol_game_guard + WHERE server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, DockerPortHost] + " + SELECT server_id, port_name, port_number, protocol + FROM db_dynamic_servers.docker_ports_host + WHERE server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, ServerNomad] + " + SELECT server_id, nomad_dispatched_job_id, nomad_alloc_id, nomad_node_id, nomad_node_name, nomad_node_public_ipv4, nomad_node_vlan_ipv4, nomad_alloc_plan_ts + FROM db_dynamic_servers.server_nomad + WHERE server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, ServerPort] + " + SELECT server_id, nomad_label, nomad_ip, nomad_source + FROM db_dynamic_servers.server_ports + WHERE server_id = ANY($1) + ", + &server_ids, + ), + )?; + + let servers_proto = server_rows + .into_iter() + .map(|server| { + let metadata: std::collections::HashMap = + serde_json::from_value(server.metadata)?; + let environment: std::collections::HashMap = + serde_json::from_value(server.environment)?; + + let server_nomad = unwrap!(server_nomad_rows + .iter() + .find(|x| x.server_id == server.server_id)); + + // TODO: Handle timeout to let Traefik pull config + let is_connectable = server_nomad.nomad_alloc_plan_ts.is_some(); + + let ports = port_gg_rows + .iter() + .filter(|p| p.server_id == server.server_id) + .map(|gg_port| { + GlobalResult::Ok(( + gg_port.port_name.clone(), + create_port_gg(is_connectable, gg_port, server.datacenter_id)?, + )) + }) + .chain( + port_host_rows + .iter() + .filter(|p| p.server_id == server.server_id) + .map(|host_port| { + let server_port = server_port_rows.iter().find(|x| { + x.server_id == server.server_id + && x.nomad_label + == util_ds::format_nomad_port_label(&host_port.port_name) + }); + GlobalResult::Ok(( + host_port.port_name.clone(), + create_port_host(is_connectable, host_port, server_port)?, + )) + }), + ) + .collect::>>()?; + + let server_proto = backend::ds::Server { + server_id: Some(server.server_id.into()), + game_id: Some(server.game_id.into()), + datacenter_id: Some(server.datacenter_id.into()), + cluster_id: Some(server.cluster_id.into()), + metadata, + resources: Some(backend::ds::ServerResources { + cpu_millicores: server.resources_cpu_millicores.try_into()?, + memory_mib: server.resources_memory_mib.try_into()?, + }), + kill_timeout_ms: server.kill_timeout_ms, + args: server.args, + environment, + image_id: Some(server.image_id.into()), + network_mode: server.network_mode.try_into()?, + network_ports: ports, + create_ts: server.create_ts, + destroy_ts: server.destroy_ts, + }; + + Ok(server_proto) + }) + .collect::>>()?; + + Ok(dynamic_servers::server_get::Response { + servers: servers_proto, + }) +} + +fn create_port_gg( + is_connectable: bool, + gg_port: &DockerPortProtocolGameGuard, + datacenter_id: Uuid, +) -> GlobalResult { + Ok(backend::ds::Port { + server_port: Some(gg_port.port_number.try_into()?), + public_hostname: if is_connectable { + Some(util_ds::build_ds_hostname( + gg_port.server_id, + &gg_port.port_name, + datacenter_id, + )?) + } else { + None + }, + public_port: if is_connectable { + Some(gg_port.gg_port.try_into()?) + } else { + None + }, + routing: Some(backend::ds::port::Routing::GameGuard( + backend::ds::GameGuardRouting { + protocol: gg_port.protocol.try_into()?, + }, + )), + }) +} + +fn create_port_host( + is_connectable: bool, + host_port: &DockerPortHost, + server_port: Option<&ServerPort>, +) -> GlobalResult { + Ok(backend::ds::Port { + server_port: Some(host_port.port_number.try_into()?), + public_hostname: if is_connectable { + server_port.map(|x| x.nomad_ip.clone()) + } else { + None + }, + public_port: if is_connectable { + server_port.map(|x| x.nomad_source.try_into()).transpose()? + } else { + None + }, + routing: Some(backend::ds::port::Routing::Host(backend::ds::HostRouting { + protocol: host_port.protocol.try_into()?, + })), + }) +} diff --git a/svc/pkg/ds/ops/server-get/tests/integration.rs b/svc/pkg/ds/ops/server-get/tests/integration.rs new file mode 100644 index 000000000..94dc56d4d --- /dev/null +++ b/svc/pkg/ds/ops/server-get/tests/integration.rs @@ -0,0 +1,79 @@ +use std::collections::HashMap; + +use chirp_worker::prelude::*; +use proto::backend::{self, pkg::dynamic_servers}; + +#[worker_test] +async fn server_get(ctx: TestCtx) { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await + .unwrap(); + let game_id = game_res.game_id.unwrap(); + + // Pick an existing cluster + let cluster_id = op!([ctx] cluster_list {}) + .await + .unwrap() + .cluster_ids + .first() + .unwrap() + .to_owned(); + + let build_res: backend::pkg::faker::build::Response = op!([ctx] faker_build { + game_id: Some(game_id), + image: backend::faker::Image::DsEcho as i32, + }) + .await + .unwrap(); + + let faker_region = op!([ctx] faker_region {}).await.unwrap(); + + let env = vec![ + ("some_envkey_test".to_string(), "2134523".to_string()), + ( + "some_other_envkey_test".to_string(), + "4325234356".to_string(), + ), + ] + .into_iter() + .collect(); + + let ports = vec![( + "testing2".to_string(), + dynamic_servers::server_create::Port { + server_port: Some(28234), + routing: Some(dynamic_servers::server_create::port::Routing::GameGuard( + backend::ds::GameGuardRouting { protocol: 0 }, + )), + }, + )] + // Collect into hashmap + .into_iter() + .collect(); + + let server = op!([ctx] ds_server_create { + game_id: Some(game_id), + cluster_id: Some(cluster_id), + datacenter_id: faker_region.region_id, + resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + kill_timeout_ms: 0, + metadata: HashMap::new(), + args: Vec::new(), + environment: env, + image_id: Some(build_res.build_id.unwrap()), + network_mode: 0, + network_ports: ports, + }) + .await + .unwrap() + .server + .unwrap(); + + let server_res = op!([ctx] ds_server_get { + server_ids: vec![server.server_id.unwrap()], + }) + .await + .unwrap(); +} diff --git a/svc/pkg/ds/types/server-create.proto b/svc/pkg/ds/types/server-create.proto index d64d2e58f..ebda435bd 100644 --- a/svc/pkg/ds/types/server-create.proto +++ b/svc/pkg/ds/types/server-create.proto @@ -3,21 +3,33 @@ syntax = "proto3"; package rivet.backend.pkg.dynamic_servers.server_create; import "proto/common.proto"; -import "proto/backend/dynamic_servers.proto"; +import "proto/backend/ds.proto"; message Request { rivet.common.Uuid game_id = 1; rivet.common.Uuid cluster_id = 2; rivet.common.Uuid datacenter_id = 3; - rivet.backend.dynamic_servers.ServerResources resources = 4; + rivet.backend.ds.ServerResources resources = 4; int64 kill_timeout_ms = 5; map metadata = 6; - - oneof runtime { - rivet.backend.dynamic_servers.DockerRuntime docker_runtime = 7; - } + repeated string args = 7; + map environment = 8; + rivet.common.Uuid image_id = 9; + rivet.backend.ds.NetworkMode network_mode = 10; + map network_ports = 11; } message Response { - rivet.backend.dynamic_servers.Server server = 1; + rivet.backend.ds.Server server = 1; +} + +message Port { + // Null when using host networking since one is automatially assigned + optional int32 server_port = 1; + + oneof routing { + rivet.backend.ds.GameGuardRouting game_guard = 101; + rivet.backend.ds.HostRouting host = 102; + } } + diff --git a/svc/pkg/ds/types/server-get.proto b/svc/pkg/ds/types/server-get.proto new file mode 100644 index 000000000..f40b860a8 --- /dev/null +++ b/svc/pkg/ds/types/server-get.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package rivet.backend.pkg.dynamic_servers.server_get; + +import "proto/common.proto"; +import "proto/backend/ds.proto"; + +message Request { + repeated rivet.common.Uuid server_ids = 1; +} + +message Response { + repeated rivet.backend.ds.Server servers = 1; +} diff --git a/svc/pkg/ds/util/Cargo.toml b/svc/pkg/ds/util/Cargo.toml index b486fae71..57949c5e4 100644 --- a/svc/pkg/ds/util/Cargo.toml +++ b/svc/pkg/ds/util/Cargo.toml @@ -20,4 +20,4 @@ uuid = { version = "1", features = ["v4", "serde"] } ip-info = { path = "../../ip/ops/info" } mm-lobby-list-for-user-id = { path = "../../mm/ops/lobby-list-for-user-id" } region-get = { path = "../../region/ops/get" } -user-identity-get = { path = "../../user-identity/ops/get" } \ No newline at end of file +user-identity-get = { path = "../../user-identity/ops/get" } diff --git a/svc/pkg/ds/util/src/defaults.rs b/svc/pkg/ds/util/src/defaults.rs deleted file mode 100644 index 1c85d6df0..000000000 --- a/svc/pkg/ds/util/src/defaults.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub const TIER_NAME_ID: &str = "basic-1d1"; -pub const MAX_PLAYERS_NORMAL: u32 = 32; -pub const MAX_PLAYERS_DIRECT: u32 = 40; -pub const MAX_PLAYERS_PARTY: u32 = 40; diff --git a/svc/pkg/ds/util/src/key.rs b/svc/pkg/ds/util/src/key.rs deleted file mode 100644 index ef03e569a..000000000 --- a/svc/pkg/ds/util/src/key.rs +++ /dev/null @@ -1,209 +0,0 @@ -use uuid::Uuid; - -use crate::JoinKind; - -/// HASH -pub fn player_config(player_id: Uuid) -> String { - format!("{{global}}:mm:player:{}:config", player_id) -} - -pub mod player_config { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct Config { - #[serde(rename = "l")] - pub lobby_id: Uuid, - #[serde(rename = "qi")] - pub query_id: Option, - #[serde(rename = "ra")] - pub remote_address: String, - } - - pub const LOBBY_ID: &str = "l"; - pub const QUERY_ID: &str = "qi"; - pub const REMOTE_ADDRESS: &str = "ra"; -} - -/// HASH -pub fn lobby_config(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:config", lobby_id) -} - -/// HASH -pub fn lobby_tags(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:tags", lobby_id) -} - -pub mod lobby_config { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct Config { - #[serde(rename = "ns")] - pub namespace_id: Uuid, - #[serde(rename = "r")] - pub region_id: Uuid, - #[serde(rename = "lg")] - pub lobby_group_id: Uuid, - #[serde(rename = "mpn")] - pub max_players_normal: u32, - #[serde(rename = "mpp")] - pub max_players_party: u32, - #[serde(rename = "mpd")] - pub max_players_direct: u32, - #[serde(rename = "p")] - pub preemptive: bool, - #[serde(rename = "rt", skip_serializing_if = "Option::is_none")] - pub ready_ts: Option, - #[serde(rename = "c")] - pub is_closed: bool, - #[serde(rename = "cu")] - pub is_custom: bool, - #[serde(rename = "st", skip_serializing_if = "Option::is_none")] - pub state_json: Option, - } - - pub const NAMESPACE_ID: &str = "ns"; - pub const REGION_ID: &str = "r"; - pub const LOBBY_GROUP_ID: &str = "lg"; - pub const MAX_PLAYERS_NORMAL: &str = "mpn"; - pub const MAX_PLAYERS_PARTY: &str = "mpp"; - pub const MAX_PLAYERS_DIRECT: &str = "mpd"; - pub const PREEMPTIVE: &str = "p"; - pub const READY_TS: &str = "rt"; - pub const IS_CLOSED: &str = "c"; - pub const IS_CUSTOM: &str = "cu"; - pub const STATE_JSON: &str = "st"; -} - -/// HASH -/// -/// Includes the state of all active find queries. -pub fn find_query_state(query_id: Uuid) -> String { - format!("{{global}}:mm:find_query:{}:state", query_id) -} - -pub mod find_query_state { - use uuid::Uuid; - - #[derive(Debug, serde::Serialize)] - pub struct State { - #[serde(rename = "n")] - pub namespace_id: Uuid, - #[serde(rename = "l", skip_serializing_if = "Option::is_none")] - pub lobby_id: Option, - #[serde(rename = "lac", skip_serializing_if = "Option::is_none")] - pub lobby_auto_created: Option, - #[serde(rename = "s")] - pub status: u8, - } - - pub const NAMESPACE_ID: &str = "n"; - pub const PLAYER_IDS: &str = "pl"; - pub const LOBBY_ID: &str = "l"; - pub const LOBBY_AUTO_CREATED: &str = "lac"; - pub const STATUS: &str = "s"; -} - -/// SET -pub fn find_query_player_ids(query_id: Uuid) -> String { - format!("{{global}}:mm:find_query:{}:player_ids", query_id) -} - -/// ZSET -/// -/// Includes all active find queries for a lobby. -pub fn lobby_find_queries(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:find_queries", lobby_id) -} - -/// ZSET -pub fn ns_player_ids(namespace_id: Uuid) -> String { - format!("{{global}}:mm:ns:{}:player_ids", namespace_id) -} - -/// ZSET -pub fn ns_lobby_ids(namespace_id: Uuid) -> String { - format!("{{global}}:mm:ns:{}:lobby_ids", namespace_id) -} - -/// SET -pub fn ns_remote_address_player_ids(namespace_id: Uuid, remote_address: &str) -> String { - format!( - "{{global}}:mm:ns:{}:remote_address:{}:player_ids", - namespace_id, remote_address - ) -} - -/// ZSET -pub fn lobby_player_ids(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:player_ids", lobby_id) -} - -/// ZSET -pub fn lobby_registered_player_ids(lobby_id: Uuid) -> String { - format!("{{global}}:mm:lobby:{}:registered_player_ids", lobby_id) -} - -/// ZSET -pub fn idle_lobby_ids(namespace_id: Uuid, region_id: Uuid, lobby_group_id: Uuid) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lg:{}:idle_lobby_ids", - namespace_id, region_id, lobby_group_id - ) -} - -/// Map containing all idle lobbies and their associated lobby group -/// IDs. -/// -/// We limit this to just idle lobbies since we need to iterate over all -/// the values in this hash in mm-lobby-idle-update, so we want to limit -/// the values in here as much as possible. -/// -/// We keep this all in one hash so we only have to lock one key instead -/// of using `SCAN`. -/// -/// HASH -pub fn idle_lobby_lobby_group_ids(namespace_id: Uuid, region_id: Uuid) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lobby:idle:lobby_group_ids", - namespace_id, region_id, - ) -} - -/// ZSET -pub fn lobby_available_spots( - namespace_id: Uuid, - region_id: Uuid, - lobby_group_id: Uuid, - join_kind: JoinKind, -) -> String { - format!( - "{{global}}:mm:ns:{}:region:{}:lg:{}:lobby:available_spots:{}", - namespace_id, - region_id, - lobby_group_id, - join_kind.short() - ) -} - -/// ZSET -pub fn lobby_unready() -> String { - "{global}:mm:lobby:unready".to_string() -} - -/// ZSET -pub fn player_unregistered() -> String { - "{global}:mm:player:unregistered".to_string() -} - -/// ZSET -pub fn player_auto_remove() -> String { - "{global}:mm:player:auto_remove".to_string() -} - -// Placeholder key -pub fn empty() -> String { - "{global}".to_string() -} diff --git a/svc/pkg/ds/util/src/lib.rs b/svc/pkg/ds/util/src/lib.rs index ac75d4f9f..3437193c7 100644 --- a/svc/pkg/ds/util/src/lib.rs +++ b/svc/pkg/ds/util/src/lib.rs @@ -1,45 +1,32 @@ pub mod consts; -pub mod defaults; -pub mod key; pub mod test; -pub mod verification; -pub mod version_migrations; -pub enum JoinKind { - Normal, - Party, - Direct, -} - -impl JoinKind { - pub fn short(self) -> &'static str { - match self { - JoinKind::Normal => "normal", - JoinKind::Party => "party", - JoinKind::Direct => "direct", - } - } -} +use rivet_operation::prelude::*; -#[derive(Debug, PartialEq, strum::FromRepr)] -#[repr(u8)] -pub enum FindQueryStatus { - /// Lobby is creating or in between mm-lobby-find and - /// mm-lobby-find-try-complete. - Pending = 0, - /// Find finished and lobby is ready. - Complete = 1, - /// There was an error. - Fail = 2, +pub fn build_ds_hostname( + server_id: Uuid, + port_name: &str, + datacenter_id: Uuid, +) -> GlobalResult { + // TODO: Change lobby -> server + Ok(format!( + "{}-{}.lobby.{}.{}", + server_id, + port_name, + datacenter_id, + unwrap!(rivet_util::env::domain_job()), + )) } /// Formats the port label to be used in Nomad. /// /// Prefixing this port ensure that the user defined port names don't interfere /// with other ports. +/// +/// See also SQL `concat` in `svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs`. pub fn format_nomad_port_label(port_label: &str) -> String { let snake_port_label = heck::SnakeCase::to_snake_case(port_label); - format!("game_{snake_port_label}") + format!("ds_{snake_port_label}") } pub const RUNC_SETUP_CPU: i32 = 50; diff --git a/svc/pkg/ds/util/src/verification.rs b/svc/pkg/ds/util/src/verification.rs deleted file mode 100644 index ae51d87f5..000000000 --- a/svc/pkg/ds/util/src/verification.rs +++ /dev/null @@ -1,391 +0,0 @@ -use std::collections::HashMap; - -use futures_util::{StreamExt, TryStreamExt}; -use http::StatusCode; -use proto::backend::{self, pkg::*}; -use rivet_operation::prelude::*; -use serde::Serialize; -use uuid::Uuid; - -#[derive(Serialize)] -pub struct ExternalVerificationRequest { - pub verification_data: Option, - pub game: Game, - pub clients: HashMap, - pub join_kind: JoinKind, - pub kind: ConnectionKind, -} - -#[derive(Serialize)] -pub struct Game { - pub namespace_id: Uuid, - pub game_mode_id: Uuid, - pub game_mode_name_id: String, - - pub lobby: Option, - pub state: Option, - pub config: Option, - pub tags: HashMap, - pub dynamic_max_players: Option, -} - -#[derive(Serialize)] -pub struct Lobby { - pub lobby_id: Uuid, - pub region_id: Uuid, - pub region_name_id: String, - pub create_ts: String, - pub is_closed: bool, -} - -#[derive(Serialize)] -pub struct Client { - pub user_agent: Option, - pub latitude: Option, - pub longitude: Option, -} - -#[derive(Serialize)] -pub enum JoinKind { - Normal, - Party, -} - -#[derive(Copy, Clone, Serialize)] -pub enum ConnectionKind { - Find, - Join, - Create, -} - -pub struct VerifyConfigOpts<'a> { - pub kind: ConnectionKind, - pub namespace_id: Uuid, - pub user_id: Option, - pub client_info: Vec, - pub tags: &'a HashMap, - pub dynamic_max_players: Option, - - pub lobby_groups: &'a [backend::matchmaker::LobbyGroup], - pub lobby_group_meta: &'a [backend::matchmaker::LobbyGroupMeta], - pub lobby_info: Option<&'a backend::matchmaker::Lobby>, - pub lobby_state_json: Option<&'a str>, - - pub verification_data_json: Option<&'a str>, - pub lobby_config_json: Option<&'a str>, - pub custom_lobby_publicity: Option, -} - -struct ExternalRequestConfigAndLobby<'a> { - pub lobby_group: &'a backend::matchmaker::LobbyGroup, - pub lobby_group_meta: &'a backend::matchmaker::LobbyGroupMeta, - external_request_config: backend::net::ExternalRequestConfig, -} - -/// Verifies everything required to make a find request or create a custom lobby. -pub async fn verify_config( - ctx: &OperationContext<()>, - opts: &VerifyConfigOpts<'_>, -) -> GlobalResult<()> { - let mut highest_identity_requirement = backend::matchmaker::IdentityRequirement::None; - let mut external_request_configs = Vec::new(); - - // Collect all external request configs and identity requirement - for (lobby_group, lobby_group_meta) in opts.lobby_groups.iter().zip(opts.lobby_group_meta) { - let (identity_requirement, external_request_config) = match ( - opts.kind, - lobby_group.actions.as_ref().and_then(|a| a.find.as_ref()), - lobby_group.actions.as_ref().and_then(|a| a.join.as_ref()), - lobby_group.actions.as_ref().and_then(|a| a.create.as_ref()), - ) { - (ConnectionKind::Find, Some(find_config), _, _) => { - if !find_config.enabled { - bail_with!(MATCHMAKER_FIND_DISABLED); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - find_config.identity_requirement - ), - "invalid identity requirement variant" - ), - find_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Join, _, Some(join_config), _) => { - if !join_config.enabled { - bail_with!(MATCHMAKER_JOIN_DISABLED); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - join_config.identity_requirement - ), - "invalid identity requirement variant" - ), - join_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Create, _, _, Some(create_config)) => { - let publicity = unwrap!(opts.custom_lobby_publicity); - - // Verify publicity - match ( - publicity, - create_config.enable_public, - create_config.enable_private, - ) { - (backend::matchmaker::lobby::Publicity::Public, allowed, _) => { - ensure_with!( - allowed, - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = r#""public" publicity not allowed with this custom game mode"# - ); - } - (backend::matchmaker::lobby::Publicity::Private, _, allowed) => { - ensure_with!( - allowed, - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = - r#""private" publicity not allowed with this custom game mode"# - ); - } - } - - // Verify lobby count - if let (Some(max_lobbies_per_identity), Some(user_id)) = - (create_config.max_lobbies_per_identity, opts.user_id) - { - let lobbies_res = op!([ctx] mm_lobby_list_for_user_id { - user_ids: vec![user_id.into()], - }) - .await?; - let user = unwrap!(lobbies_res.users.first()); - ensure_with!( - (user.lobby_ids.len() as u64) < max_lobbies_per_identity, - MATCHMAKER_CUSTOM_LOBBY_LIMIT_REACHED - ); - } - - ( - unwrap!( - backend::matchmaker::IdentityRequirement::from_i32( - create_config.identity_requirement - ), - "invalid identity requirement variant" - ), - create_config.verification.as_ref().map(|config| { - backend::net::ExternalRequestConfig { - url: config.url.clone(), - method: backend::net::HttpMethod::Post as i32, - headers: config.headers.clone(), - } - }), - ) - } - (ConnectionKind::Create, _, _, None) => { - bail_with!(MATCHMAKER_CUSTOM_LOBBIES_DISABLED); - } - _ => (backend::matchmaker::IdentityRequirement::None, None), - }; - - // Updated highest requirement - match highest_identity_requirement { - backend::matchmaker::IdentityRequirement::None => { - highest_identity_requirement = identity_requirement; - } - backend::matchmaker::IdentityRequirement::Guest => { - if matches!( - identity_requirement, - backend::matchmaker::IdentityRequirement::Registered - ) { - highest_identity_requirement = identity_requirement; - } - } - backend::matchmaker::IdentityRequirement::Registered => {} - } - - if let Some(external_request_config) = external_request_config { - external_request_configs.push(ExternalRequestConfigAndLobby { - lobby_group, - lobby_group_meta, - external_request_config, - }); - } - } - - // Verify identity requirement - match (highest_identity_requirement, opts.user_id) { - (backend::matchmaker::IdentityRequirement::Registered, Some(user_id)) => { - let user_identities_res = op!([ctx] user_identity_get { - user_ids: vec![user_id.into()], - }) - .await?; - let user = unwrap!( - user_identities_res.users.first(), - "could not find user identities" - ); - let is_registered = !user.identities.is_empty(); - - if !is_registered { - bail_with!(MATCHMAKER_REGISTRATION_REQUIRED); - } - } - ( - backend::matchmaker::IdentityRequirement::Guest - | backend::matchmaker::IdentityRequirement::Registered, - None, - ) => { - bail_with!(MATCHMAKER_IDENTITY_REQUIRED); - } - _ => {} - } - - // Verify lobby config - if let Some(lobby_config_json) = opts.lobby_config_json { - ensure_with!( - lobby_config_json.len() as u64 <= util::file_size::kibibytes(16), - MATCHMAKER_CUSTOM_LOBBY_CONFIG_INVALID, - reason = "too large (> 16KiB)" - ); - } - - // Verify user data externally - for external_request_config_and_lobby in external_request_configs { - let ExternalRequestConfigAndLobby { - lobby_group, - lobby_group_meta, - external_request_config, - } = external_request_config_and_lobby; - - // Build lobby info - let lobby = if let Some(l) = &opts.lobby_info { - // Fetch region data for readable name - let region_id = unwrap!(l.region_id); - let regions_res = op!([ctx] region_get { - region_ids: vec![region_id], - }) - .await?; - let region = unwrap!(regions_res.regions.first()); - - Some(Lobby { - lobby_id: unwrap_ref!(l.lobby_id).as_uuid(), - region_id: region_id.as_uuid(), - region_name_id: region.name_id.clone(), - create_ts: util::timestamp::to_string(l.create_ts)?, - is_closed: l.is_closed, - }) - } else { - None - }; - - // Fetch IP info - let clients = futures_util::stream::iter( - opts.client_info - .iter() - .filter_map(|client_info| { - client_info - .remote_address - .as_ref() - .map(|ip| (ip.clone(), client_info.user_agent.clone())) - }) - .collect::>(), - ) - .map(|(ip, user_agent)| async move { - let ip_res = op!([ctx] ip_info { - ip: ip.clone(), - }) - .await?; - let (latitude, longitude) = ip_res - .ip_info - .and_then(|ip_info| ip_info.coords) - .map(|coords| (coords.latitude, coords.longitude)) - .unzip(); - - GlobalResult::Ok(( - ip.clone(), - Client { - user_agent: user_agent.clone(), - longitude, - latitude, - }, - )) - }) - .buffer_unordered(16) - .try_collect::>() - .await?; - - // Build body - let body = ExternalVerificationRequest { - verification_data: opts - .verification_data_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - game: Game { - game_mode_id: unwrap_ref!(lobby_group_meta.lobby_group_id).as_uuid(), - game_mode_name_id: lobby_group.name_id.clone(), - namespace_id: opts.namespace_id, - - lobby, - state: opts - .lobby_state_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - config: opts - .lobby_config_json - .as_ref() - .map(|json| serde_json::from_str::(json)) - .transpose()?, - tags: opts.tags.clone(), - dynamic_max_players: opts.dynamic_max_players, - }, - clients, - join_kind: JoinKind::Normal, - kind: opts.kind, - }; - - // Send request - let request_id = Uuid::new_v4(); - let external_res = msg!([ctx] external::msg::request_call(request_id) - -> Result - { - request_id: Some(request_id.into()), - config: Some(external_request_config), - timeout: util::duration::seconds(10) as u64, - body: Some(serde_json::to_vec(&body)?), - ..Default::default() - }) - .await?; - - // Handle status code - if let Ok(res) = external_res { - let status = StatusCode::from_u16(res.status_code as u16)?; - - tracing::info!(?status, "user verification response"); - - if !status.is_success() { - bail_with!(MATCHMAKER_VERIFICATION_FAILED); - } - } else { - bail_with!(MATCHMAKER_VERIFICATION_REQUEST_FAILED); - } - } - - Ok(()) -} diff --git a/svc/pkg/ds/util/src/version_migrations.rs b/svc/pkg/ds/util/src/version_migrations.rs deleted file mode 100644 index ed2064c68..000000000 --- a/svc/pkg/ds/util/src/version_migrations.rs +++ /dev/null @@ -1,8 +0,0 @@ -use bit_vec::BitVec; - -pub const PORT_RANGE_PROXY_IDX: usize = 0; - -/// The bit flags expected for a game version with all migrations applied. -pub fn all() -> BitVec { - BitVec::from_elem(1, true) -}