Add cli character:copy-character and #copycharacter

This commit is contained in:
Akkadius 2020-07-18 21:07:22 -05:00
parent 880b19cc7f
commit 2e0c892b07
8 changed files with 270 additions and 18 deletions

View File

@ -2309,3 +2309,141 @@ int Database::GetInstanceID(uint32 char_id, uint32 zone_id) {
return 0;
}
/**
* @param source_character_name
* @param destination_character_name
* @param destination_account_name
* @return
*/
bool Database::CopyCharacter(
std::string source_character_name,
std::string destination_character_name,
std::string destination_account_name
)
{
auto results = QueryDatabase(
fmt::format(
"SELECT id FROM character_data WHERE name = '{}' and deleted_at is NULL LIMIT 1",
source_character_name
)
);
if (results.RowCount() == 0) {
LogError("No character found with name [{}]", source_character_name);
}
auto row = results.begin();
std::string source_character_id = row[0];
results = QueryDatabase(
fmt::format(
"SELECT id FROM account WHERE name = '{}' LIMIT 1",
destination_account_name
)
);
if (results.RowCount() == 0) {
LogError("No account found with name [{}]", destination_account_name);
}
row = results.begin();
std::string source_account_id = row[0];
/**
* Fresh ID
*/
results = QueryDatabase("SELECT (MAX(id) + 1) as new_id from character_data");
row = results.begin();
std::string new_character_id = row[0];
TransactionBegin();
for (const auto &iter : DatabaseSchema::GetCharacterTables()) {
std::string table_name = iter.first;
std::string character_id_column_name = iter.second;
/**
* Columns
*/
results = QueryDatabase(fmt::format("SHOW COLUMNS FROM {}", table_name));
std::vector<std::string> columns = {};
int column_count = 0;
for (row = results.begin(); row != results.end(); ++row) {
columns.emplace_back(row[0]);
column_count++;
}
results = QueryDatabase(
fmt::format(
"SELECT {} FROM {} WHERE {} = {}",
implode(",", wrap(columns, "`")),
table_name,
character_id_column_name,
source_character_id
)
);
std::vector<std::vector<std::string>> new_rows;
for (row = results.begin(); row != results.end(); ++row) {
std::vector<std::string> new_values = {};
for (int column_index = 0; column_index < column_count; column_index++) {
std::string column = columns[column_index];
std::string value = row[column_index] ? row[column_index] : "null";
if (column == character_id_column_name) {
value = new_character_id;
}
if (column == "name" && table_name == "character_data") {
value = destination_character_name;
}
if (column == "account_id" && table_name == "character_data") {
value = source_account_id;
}
new_values.emplace_back(value);
}
new_rows.emplace_back(new_values);
}
std::string insert_values;
std::vector<std::string> insert_rows;
for (auto &r: new_rows) {
std::string insert_row = "(" + implode(",", wrap(r, "'")) + ")";
insert_rows.emplace_back(insert_row);
}
if (!insert_rows.empty()) {
QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
table_name,
character_id_column_name,
new_character_id
)
);
auto insert = QueryDatabase(
fmt::format(
"INSERT INTO {} ({}) VALUES {}",
table_name,
implode(",", wrap(columns, "`")),
implode(",", insert_rows)
)
);
if (!insert.ErrorMessage().empty()) {
TransactionRollback();
return false;
break;
}
}
}
TransactionCommit();
return true;
}

View File

@ -105,16 +105,35 @@ public:
/* Character Creation */
bool AddToNameFilter(const char* name);
bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face);
bool DeleteCharacter(char* character_name);
bool MoveCharacterToZone(const char* charname, uint32 zone_id);
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
bool ReserveName(uint32 account_id, char* name);
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
bool SetHackerFlag(const char* accountname, const char* charactername, const char* hacked);
bool SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone);
bool UpdateName(const char* oldname, const char* newname);
bool AddToNameFilter(const char *name);
bool CreateCharacter(
uint32 account_id,
char *name,
uint16 gender,
uint16 race,
uint16 class_,
uint8 str,
uint8 sta,
uint8 cha,
uint8 dex,
uint8 int_,
uint8 agi,
uint8 wis,
uint8 face
);
bool DeleteCharacter(char *character_name);
bool MoveCharacterToZone(const char *charname, uint32 zone_id);
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
bool ReserveName(uint32 account_id, char *name);
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp);
bool SetHackerFlag(const char *accountname, const char *charactername, const char *hacked);
bool SetMQDetectionFlag(const char *accountname, const char *charactername, const char *hacked, const char *zone);
bool UpdateName(const char *oldname, const char *newname);
bool CopyCharacter(
std::string source_character_name,
std::string destination_character_name,
std::string destination_account_name
);
/* General Information Queries */

View File

@ -35,7 +35,7 @@
#define va_copy(d,s) ((d) = (s))
#endif
// original source:
// original source:
// https://github.com/facebook/folly/blob/master/folly/String.cpp
//
const std::string vStringFormat(const char* format, va_list args)
@ -144,6 +144,23 @@ std::string implode(std::string glue, std::vector<std::string> src)
return final_output;
}
std::vector<std::string> wrap(std::vector<std::string> &src, std::string character)
{
std::vector<std::string> new_vector;
new_vector.reserve(src.size());
for (auto &e: src) {
if (e == "null") {
new_vector.emplace_back(e);
continue;
}
new_vector.emplace_back(character + e + character);
}
return new_vector;
}
std::string EscapeString(const std::string &s) {
std::string ret;

View File

@ -42,6 +42,7 @@ const std::string ucfirst(std::string s);
std::vector<std::string> split(std::string str_to_split, char delimiter);
const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
std::vector<std::string> wrap(std::vector<std::string> &src, std::string character);
std::string implode(std::string glue, std::vector<std::string> src);
std::string convert2digit(int n, std::string suffix);
std::string numberToWords(unsigned long long int n);
@ -98,14 +99,14 @@ std::string implode(const std::string &glue, const std::pair<char, char> &encaps
}
std::ostringstream oss;
for (const T &src_iter : src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
@ -121,7 +122,7 @@ std::vector<std::string> join_pair(const std::string &glue, const std::pair<char
for (const std::pair<T1, T2> &src_iter : src) {
output.push_back(
fmt::format(
"{}{}{}{}{}{}{}",
encapsulation.first,
@ -151,7 +152,7 @@ std::vector<std::string> join_tuple(const std::string &glue, const std::pair<cha
for (const std::tuple<T1, T2, T3, T4> &src_iter : src) {
output.push_back(
fmt::format(
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
encapsulation.first,

View File

@ -54,6 +54,7 @@ namespace WorldserverCommandHandler {
* Register commands
*/
function_map["world:version"] = &WorldserverCommandHandler::Version;
function_map["character:copy-character"] = &WorldserverCommandHandler::CopyCharacter;
function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion;
function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus;
function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema;
@ -240,12 +241,12 @@ namespace WorldserverCommandHandler {
"--compress"
};
if (argc < 3 || cmd[{"-h", "--help"}]) {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
auto database_dump_service = new DatabaseDumpService();
bool dump_all = cmd[{"-a", "--all"}];
@ -453,4 +454,45 @@ namespace WorldserverCommandHandler {
}
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void CopyCharacter(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Copies a character into a destination account";
std::vector<std::string> arguments = {
"source_character_name",
"destination_character_name",
"destination_account_name"
};
std::vector<std::string> options = { };
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
std::string source_character_name = cmd(2).str();
std::string destination_character_name = cmd(3).str();
std::string destination_account_name = cmd(4).str();
LogInfo(
"Attempting to copy character [{}] to [{}] via account [{}]",
source_character_name,
destination_character_name,
destination_account_name
);
database.CopyCharacter(
source_character_name,
destination_character_name,
destination_account_name
);
}
}

View File

@ -27,6 +27,7 @@
namespace WorldserverCommandHandler {
void CommandHandler(int argc, char **argv);
void Version(int argc, char **argv, argh::parser &cmd, std::string &description);
void CopyCharacter(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description);

View File

@ -177,6 +177,7 @@ int command_init(void)
command_add("castspell", "[spellid] - Cast a spell", 50, command_castspell) ||
command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) ||
command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) ||
command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", 250, command_copycharacter) ||
command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) ||
command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) ||
command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) ||
@ -4578,6 +4579,38 @@ void command_zonelock(Client *c, const Seperator *sep)
safe_delete(pack);
}
void command_copycharacter(Client *c, const Seperator *sep)
{
if (sep->argnum < 3) {
c->Message(
Chat::White,
"Usage: [source_character_name] [destination_character_name] [destination_account_name]"
);
return;
}
std::string source_character_name = sep->arg[1];
std::string destination_character_name = sep->arg[2];
std::string destination_account_name = sep->arg[3];
bool result = database.CopyCharacter(
source_character_name,
destination_character_name,
destination_account_name
);
c->Message(
Chat::Yellow,
fmt::format(
"Character Copy [{}] to [{}] via account [{}] [{}]",
source_character_name,
destination_character_name,
destination_account_name,
result ? "Success" : "Failed"
).c_str()
);
}
void command_corpse(Client *c, const Seperator *sep)
{
Mob *target=c->GetTarget();

View File

@ -71,6 +71,7 @@ void command_chat(Client *c, const Seperator *sep);
void command_checklos(Client *c, const Seperator *sep);
void command_clearinvsnapshots(Client *c, const Seperator *sep);
void command_connectworldserver(Client *c, const Seperator *sep);
void command_copycharacter(Client *c, const Seperator *sep);
void command_corpse(Client *c, const Seperator *sep);
void command_corpsefix(Client *c, const Seperator *sep);
void command_crashtest(Client *c, const Seperator *sep);