Skip to content

Commit

Permalink
database refactor - player data
Browse files Browse the repository at this point in the history
  • Loading branch information
Shrecknt committed Mar 6, 2024
1 parent 8aa3bc8 commit 3681d73
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 49 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/database/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
config = { workspace = true }
mowojang = { workspace = true }
sqlx = { workspace = true }
eyre = { workspace = true }
uuid = { workspace = true }
Expand Down
5 changes: 2 additions & 3 deletions crates/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ pub trait DbPush {
impl DbPush for (PingResult, Vec<PlayerInfo>) {
async fn push(&mut self, pool: &sqlx::PgPool) -> Result<(), eyre::Report> {
self.0.push(pool).await?;
let id = self.0.id;
let id = self.0.id.unwrap();
for player in &mut self.1 {
player.server = id;
player.push(pool).await?;
player.push(id, pool).await?;
}
Ok(())
}
Expand Down
97 changes: 56 additions & 41 deletions crates/database/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::{autocomplete::AutocompleteResults, DbPush};
use super::autocomplete::AutocompleteResults;
use azalea_protocol::packets::status::clientbound_status_response_packet::ClientboundStatusResponsePacket;
use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, PgPool, Row};
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, FromRow)]
Expand All @@ -11,18 +10,43 @@ pub struct PlayerInfo {
pub id: Option<i64>,
pub uuid: Uuid,
pub username: String,
#[sqlx(default)]
#[serde(skip)]
pub server: Option<i64>,
pub java_account: Option<bool>,
pub bedrock_account: Option<bool>,
}

impl PlayerInfo {
pub fn new(username: String, uuid: Uuid) -> Self {
pub fn new_unchecked(username: impl ToString, uuid: impl Into<Uuid>) -> Self {
let username = username.to_string();
let uuid = uuid.into();

Self {
id: None,
uuid,
username,
server: None,
java_account: None,
bedrock_account: None,
}
}

pub async fn new(username: impl ToString, uuid: impl Into<Uuid>) -> Self {
let username = username.to_string();
let uuid = uuid.into();

let java_account = {
let account_data = mowojang::check_uuid(uuid).await;
if let Some(account_data) = account_data {
Some(account_data.name.eq_ignore_ascii_case(&username))
} else {
None
}
};

Self {
id: None,
uuid,
username,
java_account,
bedrock_account: None,
}
}

Expand Down Expand Up @@ -61,70 +85,61 @@ impl PlayerInfo {
.unwrap()
}

pub fn from_azalea(value: &ClientboundStatusResponsePacket) -> Vec<Self> {
pub async fn from_azalea(value: &ClientboundStatusResponsePacket) -> Vec<Self> {
let mut players = Vec::with_capacity(value.players.sample.len());
for player in &value.players.sample {
let Ok(uuid) = Uuid::parse_str(&player.id) else {
continue;
};
players.push(Self {
id: None,
username: player.name.clone(),
uuid,
server: None,
});
players.push(Self::new(&player.name, uuid).await);
}
players
}
}

impl DbPush for PlayerInfo {
async fn push(&mut self, pool: &sqlx::PgPool) -> Result<(), eyre::Report> {
impl PlayerInfo {
pub async fn push(&mut self, server_id: i64, pool: &sqlx::PgPool) -> Result<(), eyre::Report> {
const QUERY: &str = "INSERT INTO players (
uuid,
username
username,
java_account,
bedrock_account,
) VALUES (
$2::UUID,
$3::TEXT
$3::TEXT,
$4::BOOLEAN,
$5::BOOLEAN
)
ON CONFLICT (uuid, username) DO NOTHING
ON CONFLICT (uuid, username) DO UPDATE SET
java_account = excluded.java_account,
bedrock_account = excluded.bedrock_account
RETURNING id";
let new_id: i64 = sqlx::query(QUERY)
.bind(self.id)
.bind(self.uuid)
.bind(&self.username)
.bind(self.java_account)
.bind(self.bedrock_account)
.fetch_one(pool)
.await?
.get("id");
self.id = Some(new_id);

if let Some(server_id) = self.server {
sqlx::query(
"INSERT INTO join_table (
sqlx::query(
"INSERT INTO join_servers_players (
server_id,
player_id,
discovered,
last_seen
player_id
) VALUES (
$1::BIGINT,
$2::BIGINT,
$3::BIGINT,
$3::BIGINT
$2::BIGINT
) ON CONFLICT (server_id, player_id) DO UPDATE SET
last_seen = excluded.last_seen
last_seen = EXTRACT(epoch from now())
",
)
.bind(server_id)
.bind(new_id)
.bind(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs() as i64,
)
.execute(pool)
.await?;
}
)
.bind(server_id)
.bind(new_id)
.execute(pool)
.await?;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/database/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl PingResult {
pub async fn from_player_id(player_id: i64, pool: &PgPool) -> Vec<Self> {
const QUERY_STRING: &str = "
WITH joins AS (
SELECT server_id FROM join_table WHERE player_id = $1::BIGINT
SELECT server_id FROM join_servers_players WHERE player_id = $1::BIGINT
) SELECT * FROM servers WHERE id IN (SELECT server_id FROM joins);
";
sqlx::query_as(QUERY_STRING)
Expand Down
2 changes: 1 addition & 1 deletion crates/io/src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Io for NetworkScanner {

if let Ok(ClientboundStatusPacket::StatusResponse(ping_response)) = clientbound_status {
let ping_result = PingResult::from_azalea(*addr.ip(), addr.port(), &ping_response);
let player_info = PlayerInfo::from_azalea(&ping_response);
let player_info = PlayerInfo::from_azalea(&ping_response).await;
self.sender.send((ping_result, player_info))?;
};

Expand Down
2 changes: 1 addition & 1 deletion crates/io/src/pnet/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub async fn start_server(mut socket: StatelessTcp, sender: Sender<(PingResult,
};
let ping_result =
PingResult::from_azalea(ip.source, tcp.source, &ping_response);
let player_info = PlayerInfo::from_azalea(&ping_response);
let player_info = PlayerInfo::from_azalea(&ping_response).await;
let _ = sender.send((ping_result, player_info));
awaiting_data_map.remove(&source_addr);
}
Expand Down
4 changes: 2 additions & 2 deletions postgres/scanner/setup.sql
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ CREATE TABLE IF NOT EXISTS players (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
uuid UUID NOT NULL,
username TEXT NOT NULL,
java_account BOOLEAN NOT NULL,
bedrock_account BOOLEAN NOT NULL,
java_account BOOLEAN,
bedrock_account BOOLEAN,
UNIQUE (uuid, username)
);

Expand Down

0 comments on commit 3681d73

Please sign in to comment.