Add /deletelvl *backup and /os delete *backup

Allows backups to be deleted from in-game.
This commit is contained in:
Goodlyay 2025-02-27 02:06:06 -08:00
parent 0eba96d656
commit 3682ba5ee5
7 changed files with 143 additions and 26 deletions

View File

@ -103,8 +103,22 @@ namespace MCGalaxy.Commands.World {
static string[] deleteHelp = new string[] { static string[] deleteHelp = new string[] {
"&T/os delete &H- Deletes your map.", "&T/os delete &H- Deletes your map.",
"&T/os delete "+CmdDeleteLvl.BACKUP_FLAG+" [backup]",
"&H -Permanently- deletes [backup] from your map.",
}; };
static void HandleDelete(Player p, string message) { static void HandleDelete(Player p, string message) {
if (message.CaselessStarts(CmdDeleteLvl.BACKUP_FLAG)) {
string[] args = message.SplitSpaces(2); //"flag", "other args"
if (args.Length == 1) {
p.Message("You must provide a backup to delete.");
p.Message("A backup is usually a number, but may also be named.");
p.Message("See &T/{0} restore &7to display backups.", commandShortcut);
return;
}
CmdDeleteLvl.UseBackup(p, p.level.name +" "+ args[1], true);
return;
}
if (message.Length > 0) { if (message.Length > 0) {
p.Message("To delete your current map, type &T/{0} delete", commandShortcut); p.Message("To delete your current map, type &T/{0} delete", commandShortcut);
return; return;

View File

@ -22,13 +22,34 @@ namespace MCGalaxy.Commands.World {
public override string name { get { return "DeleteLvl"; } } public override string name { get { return "DeleteLvl"; } }
public override string type { get { return CommandTypes.World; } } public override string type { get { return CommandTypes.World; } }
public override LevelPermission defaultRank { get { return LevelPermission.Admin; } } public override LevelPermission defaultRank { get { return LevelPermission.Admin; } }
public const string BACKUP_FLAG = "*backup";
public readonly static CommandAlias BackupAlias = new CommandAlias("DeleteBackup", BACKUP_FLAG);
public override CommandAlias[] Aliases { public override CommandAlias[] Aliases {
get { return new[] { new CommandAlias("WDelete"), new CommandAlias("WorldDelete"), new CommandAlias("WRemove") }; } get { return new[] {
new CommandAlias("WDelete"), new CommandAlias("WorldDelete"), new CommandAlias("WRemove"),
BackupAlias
}; }
} }
public override CommandPerm[] ExtraPerms {
get { return new[] { new CommandPerm(LevelPermission.Owner, "can delete backups of levels") }; }
}
public override bool MessageBlockRestricted { get { return true; } } public override bool MessageBlockRestricted { get { return true; } }
public override void Use(Player p, string message, CommandData data) { public override void Use(Player p, string message, CommandData data) {
if (message.Length == 0 || message.SplitSpaces().Length > 1) { Help(p); return; } if (message.Length == 0) { Help(p); return; }
string[] words = message.SplitSpaces(2);
if (words[0].CaselessEq(BACKUP_FLAG)) {
if (!CheckExtraPerm(p, data, 1)) return;
UseBackup(p, words.Length >= 2 ? words[1] : "", false);
return;
}
if (words.Length > 1) { Help(p); return; }
string map = Matcher.FindMaps(p, message); string map = Matcher.FindMaps(p, message);
LevelConfig cfg; LevelConfig cfg;
@ -38,11 +59,50 @@ namespace MCGalaxy.Commands.World {
if (!LevelActions.Delete(p, map)) return; if (!LevelActions.Delete(p, map)) return;
Chat.MessageGlobal("Level {0} &Swas deleted", cfg.Color + map); Chat.MessageGlobal("Level {0} &Swas deleted", cfg.Color + map);
} }
/// <summary>
/// os changes which confirmation text is displayed
/// </summary>
public static void UseBackup(Player p, string message, bool os) {
if (message.Length == 0) { HelpBackup(p); return; }
string[] words = message.SplitSpaces();
if (words.Length < 2) {
//Cannot be seen with os since two args guaranteed provided
p.Message("You must provide a level name and the backup to delete.");
p.Message("A backup is usually a number, but may also be named.");
p.Message("See &T/help restore &7to display backups.");
return;
}
bool confirmed = words.Length == 3 && words[2].CaselessEq("confirm");
string map = words[0].ToLower();
string backup = words[1].ToLower();
if (!confirmed) {
p.Message("You are about to &Wpermanently delete&S backup \"{0}\" from level \"{1}\"",
backup, map);
if (os) {
p.Message("If you are sure, type &T/os delete {0} {1} confirm", BACKUP_FLAG, backup);
} else {
// Don't use message, since they could have typed /deletebackup earth 1 derp
// and it should not tell you to type "[...] derp confirm"
p.Message("If you are sure, type &T/{0} {1} {2} confirm", BackupAlias.Trigger, map, backup);
}
return;
}
LevelActions.DeleteBackup(p, map, backup);
}
public override void Help(Player p) { public override void Help(Player p) {
p.Message("&T/DeleteLvl [level]"); p.Message("&T/DeleteLvl [level]");
p.Message("&HCompletely deletes [level] (portals, MBs, everything)"); p.Message("&HCompletely deletes [level] (portals, MBs, everything)");
p.Message("&HA backup of the level is made in the levels/deleted folder"); p.Message("&HA backup of the level is made in the levels/deleted folder");
HelpBackup(p);
} }
public static void HelpBackup(Player p) {
p.Message("&T/DeleteLvl {0} [level] [backup]", BACKUP_FLAG);
p.Message("&H-Permanently- deletes [backup] of [level].");
}
} }
} }

View File

@ -26,8 +26,7 @@ namespace MCGalaxy.Commands.World {
public override string type { get { return CommandTypes.World; } } public override string type { get { return CommandTypes.World; } }
public override bool SuperUseable { get { return false; } } public override bool SuperUseable { get { return false; } }
const string currentFlag = "*current"; const string CURRENT_FLAG = "*current";
const string latestFlag = "*latest";
public override void Use(Player p, string message, CommandData data) { public override void Use(Player p, string message, CommandData data) {
if (message.Length == 0) { LevelOperations.OutputBackups(p, p.level); return; } if (message.Length == 0) { LevelOperations.OutputBackups(p, p.level); return; }
@ -37,7 +36,7 @@ namespace MCGalaxy.Commands.World {
string backupArg = args.Length > 1 ? args[1] : args[0]; string backupArg = args.Length > 1 ? args[1] : args[0];
string path; string path;
if (backupArg == currentFlag) { if (backupArg == CURRENT_FLAG) {
path = LevelInfo.MapPath(mapArg); path = LevelInfo.MapPath(mapArg);
if (!LevelInfo.MapExists(mapArg)) { if (!LevelInfo.MapExists(mapArg)) {
if (Directory.Exists(LevelInfo.BackupBasePath(mapArg))) { if (Directory.Exists(LevelInfo.BackupBasePath(mapArg))) {
@ -49,26 +48,11 @@ namespace MCGalaxy.Commands.World {
return; return;
} }
} else { } else {
if (!Directory.Exists(LevelInfo.BackupBasePath(mapArg))) { if (!LevelInfo.GetBackupPath(p, mapArg, backupArg, out path)) return;
p.Message("Level \"{0}\" has no backups.", mapArg); return;
}
if (backupArg == latestFlag) {
int latest = LevelInfo.LatestBackup(mapArg);
if (latest == 0) {
p.Message("&WLevel \"{0}\" does not have any numbered backups, " +
"so the latest backup could not be determined.", mapArg);
return;
}
backupArg = latest.ToString();
}
path = LevelInfo.BackupFilePath(mapArg, backupArg);
}
if (!File.Exists(path)) {
p.Message("Backup \"{0}\" for {1} could not be found.", backupArg, mapArg); return;
} }
string formattedMuseumName; string formattedMuseumName;
if (backupArg == currentFlag) { if (backupArg == CURRENT_FLAG) {
formattedMuseumName = "&cMuseum &S(" + mapArg + ")"; formattedMuseumName = "&cMuseum &S(" + mapArg + ")";
} else { } else {
formattedMuseumName = "&cMuseum &S(" + mapArg + " " + backupArg + ")"; formattedMuseumName = "&cMuseum &S(" + mapArg + " " + backupArg + ")";
@ -92,9 +76,9 @@ namespace MCGalaxy.Commands.World {
public override void Help(Player p) { public override void Help(Player p) {
p.Message("&T/Museum <level> [backup]"); p.Message("&T/Museum <level> [backup]");
p.Message("&HVisits the [backup] of <level>"); p.Message("&HVisits the [backup] of <level>");
p.Message("&T/Museum <level> *latest"); p.Message("&T/Museum <level> {0}", LevelInfo.LATEST_MUSEUM_FLAG);
p.Message("&HVisits the latest backup of <level>"); p.Message("&HVisits the latest backup of <level>");
p.Message("&T/Museum <level> *current"); p.Message("&T/Museum <level> {0}", CURRENT_FLAG);
p.Message("&HVisits <level> as it is currently stored on disk."); p.Message("&HVisits <level> as it is currently stored on disk.");
p.Message("&HIf <level> is not given, the current level is used."); p.Message("&HIf <level> is not given, the current level is used.");
} }

View File

@ -185,7 +185,7 @@ namespace MCGalaxy
return true; return true;
} }
void MovePlayersToMain() { internal void MovePlayersToMain() {
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player p in players) { foreach (Player p in players) {
if (p.level == this) { if (p.level == this) {

View File

@ -174,6 +174,31 @@ namespace MCGalaxy
return true; return true;
} }
/// <summary>
/// Deletes the backup of the given map, if found.
/// Supports Commands.World.CmdMuseum.LATEST_FLAG as backup number to return latest backup path.
/// </summary>
public static void DeleteBackup(Player p, string map, string backup) {
string discard;
if (!LevelInfo.GetBackupPath(p, map, backup, out discard)) return;
foreach (Player pl in PlayerInfo.Online.Items) {
//Find if any player is in a museum of the level backup being deleted
if (pl.level.MapName.CaselessEq(map) && pl.level.IsMuseum) {
//Move everyone out of that museum.
//Since Unload does not move players if it's a museum, call it manually
pl.level.MovePlayersToMain();
pl.level.Unload(true, false);
//TODO: Only move players out of the museum if the backup being deleted actually matches their museum
//It really doesn't look like the code was designed to be able to do this
break;
}
}
Directory.Delete(LevelInfo.BackupDirPath(map, backup), true);
p.Message("Deleted backup {0}.", backup);
}
static void DeleteDatabaseTables(string map) { static void DeleteDatabaseTables(string map) {
Database.DeleteTable("Block" + map); Database.DeleteTable("Block" + map);

View File

@ -116,6 +116,38 @@ namespace MCGalaxy {
return (LatestBackup(map) + 1).ToString(); return (LatestBackup(map) + 1).ToString();
} }
public const string LATEST_MUSEUM_FLAG = "*latest";
/// <summary>
/// Returns true if a file was found for the given map with the given backup number.
/// Supports LATEST_FLAG as backup number to return latest backup path.
/// </summary>
public static bool GetBackupPath(Player p, string map, string backupNumber, out string path) {
if (!Directory.Exists(BackupBasePath(map))) {
p.Message("Level \"{0}\" has no backups.", map);
path = null;
return false;
}
if (backupNumber == LATEST_MUSEUM_FLAG) {
int latest = LatestBackup(map);
if (latest == 0) {
p.Message("&WLevel \"{0}\" does not have any numbered backups, " +
"so the latest backup could not be determined.", map);
path = null;
return false;
}
backupNumber = latest.ToString();
}
path = BackupFilePath(map, backupNumber);
if (!File.Exists(path)) {
p.Message("Backup \"{0}\" for {1} could not be found.", backupNumber, map);
return false;
}
return true;
}
/// <summary> Relative path of a level's property file </summary> /// <summary> Relative path of a level's property file </summary>
public static string PropsPath(string name) { public static string PropsPath(string name) {
return "levels/level properties/" + name + ".properties"; return "levels/level properties/" + name + ".properties";

View File

@ -195,6 +195,8 @@ namespace MCGalaxy {
/// </summary> /// </summary>
public readonly string Reflexive; public readonly string Reflexive;
//TODO: Third Person Singular Objective (Them/Him/Her)
/// <summary> /// <summary>
/// They/them uses "plural" style verbs, so this is required for grammar that sounds correct /// They/them uses "plural" style verbs, so this is required for grammar that sounds correct
/// </summary> /// </summary>