use std::{env, time::Duration};
use tonic::transport::Channel;
mod api {
tonic::include_proto!("agones.dev.sdk");
}
use api::sdk_client::SdkClient;
pub use api::{
game_server::{
status::{PlayerStatus, Port},
ObjectMeta, Spec, Status,
},
GameServer,
};
pub type WatchStream = tonic::Streaming<GameServer>;
use crate::{alpha::Alpha, beta::Beta, errors::Result};
#[inline]
fn empty() -> api::Empty {
api::Empty {}
}
#[derive(Clone)]
pub struct Sdk {
client: SdkClient<Channel>,
alpha: Alpha,
beta: Beta,
}
impl Sdk {
pub async fn new(port: Option<u16>, keep_alive: Option<Duration>) -> Result<Self> {
let addr: http::Uri = format!(
"http://localhost:{}",
port.unwrap_or_else(|| {
env::var("AGONES_SDK_GRPC_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(9357)
})
)
.parse()?;
Self::new_internal(addr, keep_alive).await
}
pub async fn new_with_host(
host: Option<String>,
port: Option<u16>,
keep_alive: Option<Duration>,
) -> Result<Self> {
let addr: http::Uri = format!(
"{}:{}",
host.unwrap_or_else(|| {
env::var("AGONES_SDK_GRPC_HOST")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or("http://localhost".to_owned())
}),
port.unwrap_or_else(|| {
env::var("AGONES_SDK_GRPC_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(9357)
})
)
.parse()?;
Self::new_internal(addr, keep_alive).await
}
async fn new_internal(addr: http::Uri, keep_alive: Option<Duration>) -> Result<Self> {
let builder = tonic::transport::channel::Channel::builder(addr)
.connect_timeout(Duration::from_secs(30))
.keep_alive_timeout(keep_alive.unwrap_or_else(|| Duration::from_secs(30)));
let channel = builder.connect_lazy();
let mut client = SdkClient::new(channel.clone());
let alpha = Alpha::new(channel.clone());
let beta = Beta::new(channel);
tokio::time::timeout(Duration::from_secs(30), async {
let mut connect_interval = tokio::time::interval(Duration::from_millis(100));
loop {
connect_interval.tick().await;
if client.get_game_server(empty()).await.is_ok() {
break;
}
}
})
.await?;
Ok(Self { client, alpha, beta })
}
#[inline]
pub fn alpha(&self) -> &Alpha {
&self.alpha
}
#[inline]
pub fn beta(&self) -> &Beta {
&self.beta
}
pub async fn ready(&mut self) -> Result<()> {
Ok(self.client.ready(empty()).await.map(|_| ())?)
}
pub async fn allocate(&mut self) -> Result<()> {
Ok(self.client.allocate(empty()).await.map(|_| ())?)
}
pub async fn shutdown(&mut self) -> Result<()> {
Ok(self.client.shutdown(empty()).await.map(|_| ())?)
}
pub fn health_check(&self) -> tokio::sync::mpsc::Sender<()> {
let mut health_client = self.clone();
let (tx, mut rx) = tokio::sync::mpsc::channel(10);
tokio::task::spawn(async move {
let health_stream = async_stream::stream! {
while rx.recv().await.is_some() {
yield empty();
}
};
let _ = health_client.client.health(health_stream).await;
});
tx
}
pub async fn set_label(
&mut self,
key: impl Into<String>,
value: impl Into<String>,
) -> Result<()> {
Ok(self
.client
.set_label(api::KeyValue {
key: key.into(),
value: value.into(),
})
.await
.map(|_| ())?)
}
pub async fn set_annotation(
&mut self,
key: impl Into<String>,
value: impl Into<String>,
) -> Result<()> {
Ok(self
.client
.set_annotation(api::KeyValue {
key: key.into(),
value: value.into(),
})
.await
.map(|_| ())?)
}
pub async fn get_gameserver(&mut self) -> Result<GameServer> {
Ok(self
.client
.get_game_server(empty())
.await
.map(|res| res.into_inner())?)
}
pub async fn reserve(&mut self, duration: Duration) -> Result<()> {
Ok(self
.client
.reserve(api::Duration {
seconds: std::cmp::max(duration.as_secs() as i64, 1),
})
.await
.map(|_| ())?)
}
pub async fn watch_gameserver(&mut self) -> Result<WatchStream> {
Ok(self
.client
.watch_game_server(empty())
.await
.map(|stream| stream.into_inner())?)
}
}