From 71b66350d2b9af28216b1e1d24d1fc583f2839b1 Mon Sep 17 00:00:00 2001
From: Shane Handley <shanehandley@fastmail.com>
Date: Wed, 13 May 2020 22:08:26 +1000
Subject: [PATCH] Write stats to the DB when a player disconnects from the
 server.

---
 server/src/events/player.rs         | 15 +++++++++++-
 server/src/persistence/stats.rs     | 36 +++++++++++++++--------------
 server/src/sys/persistence/stats.rs |  2 +-
 3 files changed, 34 insertions(+), 19 deletions(-)

diff --git a/server/src/events/player.rs b/server/src/events/player.rs
index b4836a7505..770666b246 100644
--- a/server/src/events/player.rs
+++ b/server/src/events/player.rs
@@ -1,5 +1,7 @@
 use super::Event;
-use crate::{auth_provider::AuthProvider, client::Client, state_ext::StateExt, Server};
+use crate::{
+    auth_provider::AuthProvider, client::Client, persistence, state_ext::StateExt, Server,
+};
 use common::{
     comp,
     comp::Player,
@@ -68,6 +70,17 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
             }
         }
     }
+
+    // Sync the player's character data to the database
+    if let (Some(player), Some(stats)) = (
+        state.read_storage::<Player>().get(entity),
+        state.read_storage::<comp::Stats>().get(entity),
+    ) {
+        if let Some(character_id) = player.character_id {
+            persistence::stats::update(character_id, stats, None);
+        }
+    }
+
     // Delete client entity
     if let Err(err) = state.delete_entity_recorded(entity) {
         error!("Failed to delete disconnected client: {:?}", err);
diff --git a/server/src/persistence/stats.rs b/server/src/persistence/stats.rs
index ee87315962..3faa77b7f5 100644
--- a/server/src/persistence/stats.rs
+++ b/server/src/persistence/stats.rs
@@ -4,22 +4,24 @@ use super::{establish_connection, models::StatsUpdate, schema};
 use crate::comp;
 use diesel::prelude::*;
 
-pub fn update<'a>(updates: impl Iterator<Item = (i32, &'a comp::Stats)>) {
-    use schema::stats;
+pub fn update(character_id: i32, stats: &comp::Stats, conn: Option<&SqliteConnection>) {
+    log::warn!("stats persisting...");
 
-    let connection = establish_connection();
-
-    updates.for_each(|(character_id, stats)| {
-        if let Err(error) =
-            diesel::update(stats::table.filter(schema::stats::character_id.eq(character_id)))
-                .set(&StatsUpdate::from(stats))
-                .execute(&connection)
-        {
-            log::warn!(
-                "Failed to update stats for character: {:?}: {:?}",
-                character_id,
-                error
-            )
-        }
-    });
+    if let Err(error) =
+        diesel::update(schema::stats::table.filter(schema::stats::character_id.eq(character_id)))
+            .set(&StatsUpdate::from(stats))
+            .execute(conn.unwrap_or(&establish_connection()))
+    {
+        log::warn!(
+            "Failed to update stats for character: {:?}: {:?}",
+            character_id,
+            error
+        )
+    }
+}
+
+pub fn batch_update<'a>(updates: impl Iterator<Item = (i32, &'a comp::Stats)>) {
+    let connection = &establish_connection();
+
+    updates.for_each(|(character_id, stats)| update(character_id, stats, Some(connection)));
 }
diff --git a/server/src/sys/persistence/stats.rs b/server/src/sys/persistence/stats.rs
index 9f0d9ce3c0..0c633bedfd 100644
--- a/server/src/sys/persistence/stats.rs
+++ b/server/src/sys/persistence/stats.rs
@@ -19,7 +19,7 @@ impl<'a> System<'a> for Sys {
         if scheduler.should_run() {
             timer.start();
 
-            stats::update(
+            stats::batch_update(
                 (&players, &player_stats)
                     .join()
                     .filter_map(|(player, stats)| player.character_id.map(|id| (id, stats))),