mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-14 03:11:28 +00:00
Add cli character:copy-character and #copycharacter
This commit is contained in:
parent
880b19cc7f
commit
2e0c892b07
@ -2309,3 +2309,141 @@ int Database::GetInstanceID(uint32 char_id, uint32 zone_id) {
|
|||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,16 +105,35 @@ public:
|
|||||||
|
|
||||||
/* Character Creation */
|
/* Character Creation */
|
||||||
|
|
||||||
bool AddToNameFilter(const char* name);
|
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 CreateCharacter(
|
||||||
bool DeleteCharacter(char* character_name);
|
uint32 account_id,
|
||||||
bool MoveCharacterToZone(const char* charname, uint32 zone_id);
|
char *name,
|
||||||
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
|
uint16 gender,
|
||||||
bool ReserveName(uint32 account_id, char* name);
|
uint16 race,
|
||||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
uint16 class_,
|
||||||
bool SetHackerFlag(const char* accountname, const char* charactername, const char* hacked);
|
uint8 str,
|
||||||
bool SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone);
|
uint8 sta,
|
||||||
bool UpdateName(const char* oldname, const char* newname);
|
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 */
|
/* General Information Queries */
|
||||||
|
|
||||||
|
|||||||
@ -144,6 +144,23 @@ std::string implode(std::string glue, std::vector<std::string> src)
|
|||||||
return final_output;
|
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 EscapeString(const std::string &s) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@ const std::string ucfirst(std::string s);
|
|||||||
std::vector<std::string> split(std::string str_to_split, char delimiter);
|
std::vector<std::string> split(std::string str_to_split, char delimiter);
|
||||||
const std::string StringFormat(const char* format, ...);
|
const std::string StringFormat(const char* format, ...);
|
||||||
const std::string vStringFormat(const char* format, va_list args);
|
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 implode(std::string glue, std::vector<std::string> src);
|
||||||
std::string convert2digit(int n, std::string suffix);
|
std::string convert2digit(int n, std::string suffix);
|
||||||
std::string numberToWords(unsigned long long int n);
|
std::string numberToWords(unsigned long long int n);
|
||||||
|
|||||||
@ -54,6 +54,7 @@ namespace WorldserverCommandHandler {
|
|||||||
* Register commands
|
* Register commands
|
||||||
*/
|
*/
|
||||||
function_map["world:version"] = &WorldserverCommandHandler::Version;
|
function_map["world:version"] = &WorldserverCommandHandler::Version;
|
||||||
|
function_map["character:copy-character"] = &WorldserverCommandHandler::CopyCharacter;
|
||||||
function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion;
|
function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion;
|
||||||
function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus;
|
function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus;
|
||||||
function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema;
|
function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema;
|
||||||
@ -240,12 +241,12 @@ namespace WorldserverCommandHandler {
|
|||||||
"--compress"
|
"--compress"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (cmd[{"-h", "--help"}]) {
|
||||||
if (argc < 3 || cmd[{"-h", "--help"}]) {
|
|
||||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||||
|
|
||||||
auto database_dump_service = new DatabaseDumpService();
|
auto database_dump_service = new DatabaseDumpService();
|
||||||
bool dump_all = cmd[{"-a", "--all"}];
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
namespace WorldserverCommandHandler {
|
namespace WorldserverCommandHandler {
|
||||||
void CommandHandler(int argc, char **argv);
|
void CommandHandler(int argc, char **argv);
|
||||||
void Version(int argc, char **argv, argh::parser &cmd, std::string &description);
|
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 DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
void DatabaseSetAccountStatus(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);
|
void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
|
|||||||
@ -177,6 +177,7 @@ int command_init(void)
|
|||||||
command_add("castspell", "[spellid] - Cast a spell", 50, command_castspell) ||
|
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("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("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("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("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) ||
|
command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) ||
|
||||||
@ -4578,6 +4579,38 @@ void command_zonelock(Client *c, const Seperator *sep)
|
|||||||
safe_delete(pack);
|
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)
|
void command_corpse(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
Mob *target=c->GetTarget();
|
Mob *target=c->GetTarget();
|
||||||
|
|||||||
@ -71,6 +71,7 @@ void command_chat(Client *c, const Seperator *sep);
|
|||||||
void command_checklos(Client *c, const Seperator *sep);
|
void command_checklos(Client *c, const Seperator *sep);
|
||||||
void command_clearinvsnapshots(Client *c, const Seperator *sep);
|
void command_clearinvsnapshots(Client *c, const Seperator *sep);
|
||||||
void command_connectworldserver(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_corpse(Client *c, const Seperator *sep);
|
||||||
void command_corpsefix(Client *c, const Seperator *sep);
|
void command_corpsefix(Client *c, const Seperator *sep);
|
||||||
void command_crashtest(Client *c, const Seperator *sep);
|
void command_crashtest(Client *c, const Seperator *sep);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user