"Cloud save" beginnings
authorJoann Mõndresku <joann@cernodile.com>
Mon, 13 May 2024 19:51:58 +0000 (22:51 +0300)
committerJoann Mõndresku <joann@cernodile.com>
Mon, 13 May 2024 19:51:58 +0000 (22:51 +0300)
app.py
db/.gitignore [new file with mode: 0644]
db_store.py [new file with mode: 0644]

diff --git a/app.py b/app.py
index 157df325af7eaf497623fd60573c2c80430edc9b..8e48c1c0ca4b9b4921c1d40c4ccf753822594406 100644 (file)
--- a/app.py
+++ b/app.py
@@ -12,11 +12,13 @@ from flask import request
 
 import contracts
 import events
+import db_store
 
 upgrade_cache = {}
 
 app = Flask(__name__)
 contracts.load_contracts()
+db_store.create_backups_db()
 
 @app.route('/ei/<path:subpath>', methods=['POST'])
 def ei_routes(subpath):
@@ -28,7 +30,25 @@ def ei_routes(subpath):
                ContactReq = EIProto.EggIncFirstContactRequest()
                ContactReq.ParseFromString(data)
                ContactResp = EIProto.EggIncFirstContactResponse()
-               if ContactReq.user_id in upgrade_cache:
+               Backups = db_store.get_backups(ContactReq.user_id)
+               if len(Backups) > 0:
+                       # Lets process backups - check for any forced ones first
+                       for backup in Backups:
+                               if backup[2] == True:
+                                       # Force backup found - lets serialize the payload
+                                       SaveBackup = EIProto.Backup()
+                                       try:
+                                               SaveBackup.ParseFromString(base64.b64decode(backup[3]))
+                                               SaveBackup.force_backup = True
+                                               SaveBackup.force_offer_backup = True
+                                               ContactResp.backup.CopyFrom(SaveBackup)
+                                               db_store.update_backup(backup[0], backup[3], False)
+                                               break
+                                       except:
+                                               print("Failed to force serve backup - perhaps some logic error?")
+                                               break
+                       # TODO: Check for soul eggs/eggs of prophecy and determine algorithm for "is it worth offering?"
+               elif ContactReq.user_id in upgrade_cache:
                        print("Found an unupgraded save - lets upgrade the permit level to Pro")
                        ContactResp.backup.CopyFrom(cache[ContactReq.user_id])
                        del upgrade_cache[ContactReq.user_id]
@@ -41,6 +61,9 @@ def ei_routes(subpath):
                        SaveBackup.force_backup = True
                        SaveBackup.force_offer_backup = True
                        upgrade_cache[SaveBackup.user_id] = SaveBackup
+               else:
+                       # start storing backups after permit upgrades
+                       db_store.add_backup(SaveBackup.user_id, request.form["data"].replace(" ", "+"))
        elif subpath == "get_periodicals":
                PeriodicalResp = EIProto.PeriodicalsResponse()
                for evt in events.get_active_events():
diff --git a/db/.gitignore b/db/.gitignore
new file mode 100644 (file)
index 0000000..98e6ef6
--- /dev/null
@@ -0,0 +1 @@
+*.db
diff --git a/db_store.py b/db_store.py
new file mode 100644 (file)
index 0000000..9a3b421
--- /dev/null
@@ -0,0 +1,74 @@
+# db_store.py - Providing "cloud saves" and such
+import sqlite3
+import time
+
+# TODO: Truncate last x backups from a given device ID
+def get_connection(dbName):
+       con = sqlite3.connect("db/" + dbName + ".db")
+       return con
+
+def create_backups_db():
+       FreshInstall = True
+       con = get_connection("backups")
+       cur = con.cursor()
+       try:
+               cur.execute("CREATE TABLE Backups(BackupID INTEGER PRIMARY KEY AUTOINCREMENT, DeviceID TEXT, BackupStamp BIGINT, ForceOffer BOOL, Payload TEXT)")
+       except:
+               FreshInstall = False
+       con.commit()
+       con.close()
+       return FreshInstall
+
+def get_backup(ref_id):
+       if not isinstance(ref_id, int):
+               return None
+       con = get_connection("backups")
+       cur = con.cursor()
+       res = cur.execute("SELECT Payload FROM Backups WHERE BackupID=" + str(ref_id))
+       tuple = res.fetchone()
+       con.close()
+       if tuple is None:
+               return tuple
+       else:
+               return tuple[0]
+
+def get_backups(device_id):
+       if not device_id.isalnum():
+               return []
+       con = get_connection("backups")
+       cur = con.cursor()
+       res = cur.execute("SELECT BackupID, BackupStamp, ForceOffer, Payload FROM Backups WHERE DeviceID=\"" + device_id + "\"")
+       x = res.fetchall()
+       if x is None:
+               return []
+       else:
+               return x
+
+def add_backup(device_id, b64str):
+       if not device_id.isalnum():
+               return
+       con = get_connection("backups")
+       cur = con.cursor()
+       res = cur.execute("INSERT INTO Backups(DeviceID, BackupStamp, ForceOffer, Payload) VALUES(?, ?, ?, ?)", (device_id, int(time.time()), False, b64str))
+       con.commit()
+       con.close()
+       return
+
+def update_backup(refID, b64str, Force):
+       con = get_connection("backups")
+       cur = con.cursor()
+       res = cur.execute("UPDATE Backups SET ForceOffer=?, Payload=? WHERE BackupID=" + str(refID), (Force, b64str))
+       con.commit()
+       con.close()
+       return
+
+def offer_backup_id_to_new_device(refID, device_id):
+       srcBackup = get_backup(refID)
+       if srcBackup is None:
+               return
+       add_backup(device_id, srcBackup)
+       return
+
+## MANUAL TRANSFER EXAMPLE
+## TODO: Admin API endpoint for this
+# offer_backup_id_to_new_device(2, "f7c9c95ce5f6d06a")