mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-20 03:25:49 +01:00
Moves pAI saves to the DB
This commit is contained in:
+15
-2
@@ -597,5 +597,18 @@ CREATE TABLE `2fa_secrets` (
|
||||
`date_setup` DATETIME NOT NULL DEFAULT current_timestamp(),
|
||||
`last_time` DATETIME NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`ckey`) USING BTREE
|
||||
)
|
||||
COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
|
||||
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
|
||||
|
||||
--
|
||||
-- Table structure for table `pai_saves`
|
||||
--
|
||||
CREATE TABLE `pai_saves` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`ckey` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`pai_name` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`description` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`preferred_role` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`ooc_comments` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `ckey` (`ckey`) USING BTREE
|
||||
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Updates DB from 25-25, -AffectedArc07
|
||||
# Adds a pAI saves table to the DB
|
||||
|
||||
CREATE TABLE `pai_saves` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`ckey` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`pai_name` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`description` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`preferred_role` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
`ooc_comments` LONGTEXT NULL DEFAULT '0' COLLATE 'utf8mb4_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `ckey` (`ckey`) USING BTREE
|
||||
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
|
||||
@@ -365,7 +365,7 @@
|
||||
#define INVESTIGATE_BOMB "bombs"
|
||||
|
||||
// The SQL version required by this version of the code
|
||||
#define SQL_VERSION 25
|
||||
#define SQL_VERSION 26
|
||||
|
||||
// Vending machine stuff
|
||||
#define CAT_NORMAL 1
|
||||
|
||||
@@ -357,8 +357,8 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
pai.real_name = pai.name
|
||||
pai.key = choice.key
|
||||
card.setPersonality(pai)
|
||||
for(var/datum/paiCandidate/candidate in GLOB.paiController.pai_candidates)
|
||||
if(candidate.key == choice.key)
|
||||
for(var/datum/pai_save/candidate in GLOB.paiController.pai_candidates)
|
||||
if(candidate.owner.ckey == choice.ckey)
|
||||
GLOB.paiController.pai_candidates.Remove(candidate)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Make pAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -114,6 +114,9 @@
|
||||
/// Is the client watchlisted
|
||||
var/watchlisted = FALSE
|
||||
|
||||
/// Client's pAI save
|
||||
var/datum/pai_save/pai_save
|
||||
|
||||
/client/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
// I know we will never be in a world where admins are editing client vars to let people bypass TOS
|
||||
|
||||
@@ -287,6 +287,8 @@
|
||||
|
||||
log_client_to_db(tdata) // Make sure our client exists in the DB
|
||||
|
||||
pai_save = new(src)
|
||||
|
||||
// This is where we load all of the clients stuff from the DB
|
||||
if(SSdbcore.IsConnected())
|
||||
// Load in all our client data from the DB
|
||||
@@ -449,6 +451,7 @@
|
||||
GLOB.directory -= ckey
|
||||
GLOB.clients -= src
|
||||
QDEL_NULL(chatOutput)
|
||||
QDEL_NULL(pai_save)
|
||||
if(movingmob)
|
||||
movingmob.client_mobs_in_contents -= mob
|
||||
UNSETEMPTY(movingmob.client_mobs_in_contents)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/datum/client_login_processor/pai_save
|
||||
priority = 40
|
||||
|
||||
/datum/client_login_processor/pai_save/get_query(client/C)
|
||||
return C.pai_save.get_query()
|
||||
|
||||
/datum/client_login_processor/pai_save/process_result(datum/db_query/Q, client/C)
|
||||
C.pai_save.load_data(Q)
|
||||
@@ -1,60 +1,65 @@
|
||||
/*
|
||||
name
|
||||
key
|
||||
description
|
||||
role
|
||||
comments
|
||||
ready = 0
|
||||
*/
|
||||
/datum/pai_save
|
||||
/// Client that owns the pAI
|
||||
var/client/owner
|
||||
/// pAI's name
|
||||
var/pai_name
|
||||
/// pAI's description
|
||||
var/description
|
||||
/// pAI's role
|
||||
var/role
|
||||
/// pAI's OOC comments
|
||||
var/ooc_comments
|
||||
|
||||
/datum/paiCandidate/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
/datum/pai_save/New(client/C)
|
||||
..()
|
||||
owner = C
|
||||
|
||||
/datum/paiCandidate/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
/datum/pai_save/Destroy(force, ...)
|
||||
owner = null
|
||||
GLOB.paiController.pai_candidates -= src
|
||||
return ..()
|
||||
|
||||
var/savefile/F = new /savefile(src.savefile_path(user))
|
||||
// This proc seems useless but its used by client data loading
|
||||
/datum/pai_save/proc/get_query()
|
||||
var/datum/db_query/query = SSdbcore.NewQuery("SELECT pai_name, description, preferred_role, ooc_comments FROM pai_saves WHERE ckey=:ckey", list(
|
||||
"ckey" = owner.ckey
|
||||
))
|
||||
return query
|
||||
|
||||
// Loads our data up
|
||||
/datum/pai_save/proc/load_data(datum/db_query/Q)
|
||||
while(Q.NextRow())
|
||||
pai_name = Q.item[1]
|
||||
description = Q.item[2]
|
||||
role = Q.item[3]
|
||||
ooc_comments = Q.item[4]
|
||||
|
||||
F["name"] << src.name
|
||||
F["description"] << src.description
|
||||
F["role"] << src.role
|
||||
F["comments"] << src.comments
|
||||
// Reload save from DB if the user edits it
|
||||
/datum/pai_save/proc/reload_save()
|
||||
var/datum/db_query/Q = get_query()
|
||||
if(!Q.warn_execute())
|
||||
qdel(Q)
|
||||
return
|
||||
load_data(Q)
|
||||
qdel(Q)
|
||||
|
||||
F["version"] << 1
|
||||
// Save their save to the DB
|
||||
/datum/pai_save/proc/save_to_db()
|
||||
var/datum/db_query/query = SSdbcore.NewQuery({"
|
||||
INSERT INTO pai_saves (ckey, pai_name, description, preferred_role, ooc_comments)
|
||||
VALUES (:ckey, :pai_name, :description, :preferred_role, :ooc_comments)
|
||||
ON DUPLICATE KEY UPDATE pai_name=:pai_name2, description=:description2, preferred_role=:preferred_role2, ooc_comments=:ooc_comments2
|
||||
"}, list(
|
||||
"ckey" = owner.ckey,
|
||||
"pai_name" = pai_name,
|
||||
"description" = description,
|
||||
"preferred_role" = role,
|
||||
"ooc_comments" = ooc_comments,
|
||||
"pai_name2" = pai_name,
|
||||
"description2" = description,
|
||||
"preferred_role2" = role,
|
||||
"ooc_comments2" = ooc_comments
|
||||
))
|
||||
|
||||
return 1
|
||||
|
||||
// loads the savefile corresponding to the mob's ckey
|
||||
// if silent=true, report incompatible savefiles
|
||||
// returns 1 if loaded (or file was incompatible)
|
||||
// returns 0 if savefile did not exist
|
||||
|
||||
/datum/paiCandidate/proc/savefile_load(mob/user, silent = 1)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/path = savefile_path(user)
|
||||
|
||||
if(!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(path)
|
||||
|
||||
if(!F) return //Not everyone has a pai savefile.
|
||||
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
|
||||
if(isnull(version) || version != 1)
|
||||
fdel(path)
|
||||
if(!silent)
|
||||
alert(user, "Your savefile was incompatible with this version and was deleted.")
|
||||
return 0
|
||||
|
||||
F["name"] >> src.name
|
||||
F["description"] >> src.description
|
||||
F["role"] >> src.role
|
||||
F["comments"] >> src.comments
|
||||
return 1
|
||||
query.warn_execute()
|
||||
qdel(query)
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler for pAI candidates
|
||||
|
||||
/datum/paiCandidate
|
||||
var/name
|
||||
var/key
|
||||
var/description
|
||||
var/role
|
||||
var/comments
|
||||
var/ready = 0
|
||||
|
||||
/datum/paiController
|
||||
var/list/pai_candidates = list()
|
||||
var/list/asked = list()
|
||||
@@ -18,7 +10,7 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
|
||||
/datum/paiController/Topic(href, href_list[])
|
||||
|
||||
var/datum/paiCandidate/candidate = locateUID(href_list["candidate"])
|
||||
var/datum/pai_save/candidate = locateUID(href_list["candidate"])
|
||||
|
||||
if(candidate)
|
||||
if(!istype(candidate))
|
||||
@@ -32,14 +24,14 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
return
|
||||
if(usr.incapacitated() || isobserver(usr) || !card.Adjacent(usr))
|
||||
return
|
||||
if(istype(card, /obj/item/paicard) && istype(candidate, /datum/paiCandidate))
|
||||
if(istype(card, /obj/item/paicard) && istype(candidate, /datum/pai_save))
|
||||
var/mob/living/silicon/pai/pai = new(card)
|
||||
if(!candidate.name)
|
||||
if(!candidate.pai_name)
|
||||
pai.name = pick(GLOB.ninja_names)
|
||||
else
|
||||
pai.name = candidate.name
|
||||
pai.name = candidate.pai_name
|
||||
pai.real_name = pai.name
|
||||
pai.key = candidate.key
|
||||
pai.key = candidate.owner.ckey
|
||||
|
||||
card.setPersonality(pai)
|
||||
card.looking_for_personality = 0
|
||||
@@ -64,7 +56,7 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
return
|
||||
|
||||
if(candidate)
|
||||
if(candidate.key && usr.key && candidate.key != usr.key)
|
||||
if(candidate.owner.ckey && usr.ckey && candidate.owner.ckey != usr.ckey)
|
||||
message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate and usr have different keys)")
|
||||
log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate and usr have different keys)")
|
||||
return
|
||||
@@ -75,9 +67,9 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
|
||||
switch(option)
|
||||
if("name")
|
||||
t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text
|
||||
t = input("Enter a name for your pAI", "pAI Name", candidate.pai_name) as text
|
||||
if(t)
|
||||
candidate.name = sanitize(copytext(t,1,MAX_NAME_LEN))
|
||||
candidate.pai_name = sanitize(copytext(t,1,MAX_NAME_LEN))
|
||||
if("desc")
|
||||
t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message
|
||||
if(t)
|
||||
@@ -87,26 +79,26 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
if(t)
|
||||
candidate.role = sanitize(copytext(t,1,MAX_MESSAGE_LEN))
|
||||
if("ooc")
|
||||
t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message
|
||||
t = input("Enter any OOC comments", "pAI OOC Comments", candidate.ooc_comments) as message
|
||||
if(t)
|
||||
candidate.comments = sanitize(copytext(t,1,MAX_MESSAGE_LEN))
|
||||
candidate.ooc_comments = sanitize(copytext(t,1,MAX_MESSAGE_LEN))
|
||||
if("save")
|
||||
candidate.savefile_save(usr)
|
||||
if("load")
|
||||
candidate.savefile_load(usr)
|
||||
candidate.save_to_db(usr)
|
||||
if("reload")
|
||||
candidate.reload_save(usr)
|
||||
//In case people have saved unsanitized stuff.
|
||||
if(candidate.name)
|
||||
candidate.name = sanitize(copytext(candidate.name,1,MAX_NAME_LEN))
|
||||
if(candidate.pai_name)
|
||||
candidate.pai_name = sanitize(copytext(candidate.pai_name, 1, MAX_NAME_LEN))
|
||||
if(candidate.description)
|
||||
candidate.description = sanitize(copytext(candidate.description,1,MAX_MESSAGE_LEN))
|
||||
candidate.description = sanitize(copytext(candidate.description, 1, MAX_MESSAGE_LEN))
|
||||
if(candidate.role)
|
||||
candidate.role = sanitize(copytext(candidate.role,1,MAX_MESSAGE_LEN))
|
||||
if(candidate.comments)
|
||||
candidate.comments = sanitize(copytext(candidate.comments,1,MAX_MESSAGE_LEN))
|
||||
candidate.role = sanitize(copytext(candidate.role, 1, MAX_MESSAGE_LEN))
|
||||
if(candidate.ooc_comments)
|
||||
candidate.ooc_comments = sanitize(copytext(candidate.ooc_comments, 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if("submit")
|
||||
if(candidate)
|
||||
candidate.ready = 1
|
||||
GLOB.paiController.pai_candidates |= candidate
|
||||
for(var/obj/item/paicard/p in world)
|
||||
if(p.looking_for_personality == 1)
|
||||
p.alertUpdate()
|
||||
@@ -114,18 +106,8 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
return
|
||||
recruitWindow(usr)
|
||||
|
||||
/datum/paiController/proc/recruitWindow(mob/M as mob)
|
||||
var/datum/paiCandidate/candidate
|
||||
for(var/datum/paiCandidate/c in pai_candidates)
|
||||
if(!istype(c) || !istype(M))
|
||||
break
|
||||
if(c.key == M.key)
|
||||
candidate = c
|
||||
if(!candidate)
|
||||
candidate = new /datum/paiCandidate()
|
||||
candidate.key = M.key
|
||||
pai_candidates.Add(candidate)
|
||||
|
||||
/datum/paiController/proc/recruitWindow(mob/M)
|
||||
var/datum/pai_save/candidate = M.client.pai_save
|
||||
|
||||
var/dat = ""
|
||||
dat += {"
|
||||
@@ -194,7 +176,7 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
<table>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=[UID()];option=name;new=1;candidate=[candidate.UID()]'>Name</a>:</th>
|
||||
<td class="desc">[candidate.name] </td>
|
||||
<td class="desc">[candidate.pai_name] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.</td>
|
||||
@@ -215,7 +197,7 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
</tr>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=[UID()];option=ooc;new=1;candidate=[candidate.UID()]'>OOC Comments</a>:</th>
|
||||
<td class="desc">[candidate.comments] </td>
|
||||
<td class="desc">[candidate.ooc_comments] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.</td>
|
||||
@@ -230,7 +212,7 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="button">
|
||||
<a href='byond://?src=[UID()];option=load;new=1;candidate=[candidate.UID()]' class="button">Load Personality</a>
|
||||
<a href='byond://?src=[UID()];option=reload;new=1;candidate=[candidate.UID()]' class="button">Reload Personality</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table><br>
|
||||
@@ -246,14 +228,13 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
/datum/paiController/proc/findPAI(obj/item/paicard/p, mob/user)
|
||||
requestRecruits(p, user)
|
||||
var/list/available = list()
|
||||
for(var/datum/paiCandidate/c in GLOB.paiController.pai_candidates)
|
||||
if(c.ready)
|
||||
var/found = 0
|
||||
for(var/mob/o in GLOB.respawnable_list)
|
||||
if(o.key == c.key)
|
||||
found = 1
|
||||
if(found)
|
||||
available.Add(c)
|
||||
for(var/datum/pai_save/c in GLOB.paiController.pai_candidates)
|
||||
var/found = 0
|
||||
for(var/mob/o in GLOB.respawnable_list)
|
||||
if(o.ckey == c.owner.ckey)
|
||||
found = 1
|
||||
if(found)
|
||||
available.Add(c)
|
||||
var/dat = ""
|
||||
|
||||
dat += {"
|
||||
@@ -325,12 +306,12 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
"}
|
||||
dat += "<p>Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.</p>"
|
||||
|
||||
for(var/datum/paiCandidate/c in available)
|
||||
for(var/datum/pai_save/c in available)
|
||||
dat += {"
|
||||
<table class="desc">
|
||||
<tr class="d0">
|
||||
<th>Name:</th>
|
||||
<td>[c.name]</td>
|
||||
<td>[c.pai_name]</td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<th>Description:</th>
|
||||
@@ -342,11 +323,11 @@ GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler fo
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<th>OOC Comments:</th>
|
||||
<td>[c.comments]</td>
|
||||
<td>[c.ooc_comments]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="download">
|
||||
<td class="download"><a href='byond://?src=[UID()];download=1;candidate=[c.UID()];device=\ref[p]' class="button"><b>Download [c.name]</b></a>
|
||||
<td class="download"><a href='byond://?src=[UID()];download=1;candidate=[c.UID()];device=\ref[p]' class="button"><b>Download [c.pai_name]</b></a>
|
||||
</td>
|
||||
</table>
|
||||
<br>
|
||||
|
||||
@@ -141,7 +141,7 @@ ipc_screens = [
|
||||
# Enable/disable the database on a whole
|
||||
sql_enabled = false
|
||||
# SQL version. If this is a mismatch, round start will be delayed
|
||||
sql_version = 25
|
||||
sql_version = 26
|
||||
# SQL server address. Can be an IP or DNS name
|
||||
sql_address = "127.0.0.1"
|
||||
# SQL server port
|
||||
|
||||
@@ -1384,6 +1384,7 @@
|
||||
#include "code\modules\client\login_processing\37-alts_ip.dm"
|
||||
#include "code\modules\client\login_processing\38-alts_cid.dm"
|
||||
#include "code\modules\client\login_processing\39-cid_count.dm"
|
||||
#include "code\modules\client\login_processing\40-pai_save.dm"
|
||||
#include "code\modules\client\login_processing\__client_login_processor.dm"
|
||||
#include "code\modules\client\preference\character.dm"
|
||||
#include "code\modules\client\preference\link_processing.dm"
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# AffectedArc07 2021-10-04
|
||||
import struct, os, mysql.connector, argparse, html
|
||||
from datetime import datetime
|
||||
|
||||
TYPE_NULL = 0x0
|
||||
TYPE_STRING = 0x1
|
||||
TYPE_NUMBER = 0x4
|
||||
|
||||
KEY_XOR_KEY = 0x53
|
||||
DATA_XOR_KEY = 0x3A
|
||||
|
||||
KEY_LEN_OFFSET = 8
|
||||
KEY_OFFSET = 9
|
||||
|
||||
# Might be shitcode but mehhhhhhhhhhh
|
||||
class Decoder:
|
||||
def __init__(self, data, key, jump=9):
|
||||
self.generator = self.decode_data(data, key, jump)
|
||||
|
||||
def read_bytes(self, num):
|
||||
return [next(self.generator) for _ in range(num)]
|
||||
|
||||
def read_all_bytes(self):
|
||||
result = []
|
||||
for val in self.generator:
|
||||
result.append(val)
|
||||
return result
|
||||
|
||||
def read_number(self):
|
||||
return struct.unpack("<f", bytes(self.read_bytes(4)))[0]
|
||||
|
||||
def read_string(self, length):
|
||||
return "".join([chr(c) for c in self.read_all_bytes()])
|
||||
|
||||
def decode_data(self, data, key, jump):
|
||||
for byte in data:
|
||||
yield byte ^ key
|
||||
key = (key + jump) % 256
|
||||
|
||||
|
||||
|
||||
def decode_sav(path):
|
||||
file = open(path, "rb")
|
||||
is_file_header = True
|
||||
result = {}
|
||||
|
||||
while True:
|
||||
entry_header = file.read(5)
|
||||
if len(entry_header) < 5:
|
||||
break
|
||||
|
||||
entry_length = int.from_bytes(entry_header[:-1], byteorder='little')
|
||||
|
||||
entry_data = file.read(entry_length)
|
||||
|
||||
if entry_header[4] != 1:
|
||||
continue
|
||||
|
||||
if is_file_header:
|
||||
is_file_header = False
|
||||
continue
|
||||
|
||||
key_length = entry_data[KEY_LEN_OFFSET]
|
||||
key_data = entry_data[KEY_OFFSET:KEY_OFFSET+key_length]
|
||||
key_data = Decoder(key_data, KEY_XOR_KEY).read_string(key_length)
|
||||
|
||||
if not key_data: continue
|
||||
|
||||
data_length_offset = KEY_OFFSET + key_length
|
||||
data_length = int.from_bytes(entry_data[data_length_offset:data_length_offset+4], "little")
|
||||
data_start = data_length_offset + 4
|
||||
|
||||
value = None
|
||||
|
||||
decoder = Decoder(entry_data[data_start:data_start+data_length], DATA_XOR_KEY)
|
||||
typ = decoder.read_bytes(1)[0]
|
||||
if typ == TYPE_NULL:
|
||||
value = None
|
||||
elif typ == TYPE_NUMBER:
|
||||
value = decoder.read_number()
|
||||
elif typ == TYPE_STRING:
|
||||
length = int.from_bytes(decoder.read_bytes(2), "little")
|
||||
value = decoder.read_string(length)
|
||||
|
||||
result[key_data] = value
|
||||
return result
|
||||
|
||||
# Get outer dirs
|
||||
basepath = "data/player_saves"
|
||||
|
||||
all_saves = []
|
||||
start = datetime.now()
|
||||
for dir in os.listdir(basepath):
|
||||
for dir2 in os.listdir("{}/{}".format(basepath, dir)):
|
||||
pai_data = decode_sav("{}/{}/{}/pai.sav".format(basepath, dir, dir2))
|
||||
save_data = {}
|
||||
save_data["name"] = pai_data["name"] if "name" in pai_data else None
|
||||
save_data["description"] = pai_data["description"] if "description" in pai_data else None
|
||||
save_data["role"] = pai_data["role"] if "role" in pai_data else None
|
||||
save_data["comments"] = pai_data["comments"] if "comments" in pai_data else None
|
||||
save_data["version"] = int(pai_data["version"]) if "version" in pai_data else None
|
||||
save_data["ckey"] = dir2
|
||||
all_saves.append(save_data)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("host", help="SQL server host")
|
||||
parser.add_argument("user", help="SQL login user")
|
||||
parser.add_argument("pw", help="SQL login pass")
|
||||
parser.add_argument("db", help="DB name")
|
||||
|
||||
args = parser.parse_args()
|
||||
db = mysql.connector.connect(host=args.host, user=args.user, password=args.pw, database=args.db)
|
||||
cursor = db.cursor()
|
||||
stmt = "INSERT INTO pai_saves (ckey, pai_name, description, preferred_role, ooc_comments) VALUES (%s, %s, %s, %s, %s)"
|
||||
|
||||
for entry in all_saves:
|
||||
if entry["name"]:
|
||||
entry["name"] = html.unescape(entry["name"])
|
||||
if entry["description"]:
|
||||
entry["description"] = html.unescape(entry["description"])
|
||||
if entry["role"]:
|
||||
entry["role"] = html.unescape(entry["role"])
|
||||
if entry["comments"]:
|
||||
entry["comments"] = html.unescape(entry["comments"])
|
||||
|
||||
values = (entry["ckey"], entry["name"], entry["description"], entry["role"], entry["comments"])
|
||||
cursor.execute(stmt, values)
|
||||
|
||||
db.commit()
|
||||
|
||||
duration = datetime.now() - start
|
||||
print("Complete within {}.{}s".format(duration.seconds, duration.microseconds))
|
||||
Reference in New Issue
Block a user