mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-22 12:05:51 -04:00
Improve /server backup, make compress by default
This commit is contained in:
parent
481bd1b9fd
commit
455e76ad73
@ -32,14 +32,14 @@ namespace MCGalaxy.Commands.Maintenance {
|
||||
public override void Use(Player p, string message, CommandData data) {
|
||||
string[] args = message.SplitSpaces();
|
||||
switch (args[0].ToLower()) {
|
||||
case "public": SetPublic(p, args); break;
|
||||
case "private": SetPrivate(p, args); break;
|
||||
case "reload": DoReload(p, args); break;
|
||||
case "backup": DoBackup(p, args); break;
|
||||
case "restore": DoRestore(p, args); break;
|
||||
case "import": DoImport(p, args); break;
|
||||
case "upgradeblockdb": DoBlockDBUpgrade(p, args); break;
|
||||
default: Help(p); break;
|
||||
case "public": SetPublic(p, args); break;
|
||||
case "private": SetPrivate(p, args); break;
|
||||
case "reload": DoReload(p, args); break;
|
||||
case "backup": DoBackup(p, args); break;
|
||||
case "restore": DoRestore(p, args); break;
|
||||
case "import": DoImport(p, args); break;
|
||||
case "upgradeblockdb": DoBlockDBUpgrade(p, args); break;
|
||||
default: Help(p); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,33 +56,28 @@ namespace MCGalaxy.Commands.Maintenance {
|
||||
}
|
||||
|
||||
void DoReload(Player p, string[] args) {
|
||||
if (!CheckPerms(p)) {
|
||||
p.Message("Only Console or the Server Owner can reload the server settings."); return;
|
||||
}
|
||||
p.Message("Reloading settings...");
|
||||
Server.LoadAllSettings();
|
||||
p.Message("Settings reloaded! You may need to restart the server, however.");
|
||||
Server.LoadAllSettings();
|
||||
p.Message("Settings reloaded! You may need to restart the server, however.");
|
||||
}
|
||||
|
||||
void DoBackup(Player p, string[] args) {
|
||||
string type = args.Length == 1 ? "" : args[1].ToLower();
|
||||
bool compress = true;
|
||||
if (args.Length > 2 && !CommandParser.GetBool(p, args[2], ref compress)) return;
|
||||
|
||||
if (type.Length == 0 || type == "all") {
|
||||
p.Message("Server backup started. Please wait while backup finishes.");
|
||||
Backup.CreatePackage(p, true, true, false);
|
||||
} else if (type == "database" || type == "sql" || type == "db") {
|
||||
// Creates CREATE TABLE and INSERT statements for all tables and rows in the database
|
||||
Backup.Perform(p, true, true, false, compress);
|
||||
} else if (type == "database" || type == "db") {
|
||||
p.Message("Database backup started. Please wait while backup finishes.");
|
||||
Backup.CreatePackage(p, false, true, false);
|
||||
} else if (type == "allbutdb" || type == "files" || type == "file") {
|
||||
// Saves all files and folders to a .zip
|
||||
Backup.Perform(p, false, true, false, compress);
|
||||
} else if (type == "files" || type == "file") {
|
||||
p.Message("All files backup started. Please wait while backup finishes.");
|
||||
Backup.CreatePackage(p, true, false, false);
|
||||
Backup.Perform(p, true, false, false, compress);
|
||||
} else if (type == "lite") {
|
||||
p.Message("Server backup (except BlockDB and undo data) started. Please wait while backup finishes.");
|
||||
Backup.CreatePackage(p, true, true, true);
|
||||
} else if (type == "litedb") {
|
||||
p.Message("Database backup (except BlockDB tables) started. Please wait while backup finishes.");
|
||||
Backup.CreatePackage(p, false, true, true);
|
||||
p.Message("Server backup (except BlockDB) started. Please wait while backup finishes.");
|
||||
Backup.Perform(p, true, true, true, compress);
|
||||
} else if (type == "table") {
|
||||
if (args.Length == 2) { p.Message("You need to provide the table name to backup."); return; }
|
||||
if (!Formatter.ValidName(p, args[2], "table")) return;
|
||||
@ -140,20 +135,30 @@ namespace MCGalaxy.Commands.Maintenance {
|
||||
if (ServerConfig.OwnerName.CaselessEq("Notch")) return false;
|
||||
return p.name.CaselessEq(ServerConfig.OwnerName);
|
||||
}
|
||||
|
||||
public override void Help(Player p, string message) {
|
||||
if (message.CaselessEq("backup")) {
|
||||
p.Message("%T/Server backup [mode] <compress>");
|
||||
p.Message("%HMode can be one of the following:");
|
||||
p.Message(" &fall %H- Backups everything (default)");
|
||||
p.Message(" &fdb %H- Only backups the database");
|
||||
p.Message(" &ffiles %H- Backups everything, except the database");
|
||||
p.Message(" &flite %H- Backups everything, except BlockDB files");
|
||||
p.Message("%H<compress> - Whether to compress the backup (default yes)");
|
||||
} else {
|
||||
base.Help(p, message);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Help(Player p) {
|
||||
p.Message("%T/Server reload %H- Reload the server files. (May require restart) (Owner only)");
|
||||
p.Message("%T/Server public/private %H- Make the server public or private.");
|
||||
p.Message("%T/Server restore %H- Restore the server from a backup.");
|
||||
p.Message("%T/Server backup all/db/files/lite/litedb %H- Make a backup.");
|
||||
p.Message(" %Hall - Backups everything (default)");
|
||||
p.Message(" %Hdb - Only backups the database.");
|
||||
p.Message(" %Hfiles - Backups everything, except the database.");
|
||||
p.Message(" %Hlite - Backups everything, except BlockDB and undo files.");
|
||||
p.Message(" %Hlitedb - Backups database, except BlockDB tables.");
|
||||
p.Message("%T/Server reload %H- Reloads the server files");
|
||||
p.Message("%T/Server public/private %H- Makes the server public or private");
|
||||
p.Message("%T/Server restore %H- Restores the server from a backup");
|
||||
p.Message("%T/Server backup %H- Make a backup. See %T/help server backup");
|
||||
p.Message("%T/Server backup table [name] %H- Backups that database table");
|
||||
p.Message("%T/Server import [name] %H- Imports a backed up database table");
|
||||
p.Message("%T/Server upgradeblockdb %H- Dumps BlockDB tables from database");
|
||||
p.Message("%HOnly useful when upgrading from a very old {0} version", Server.SoftwareName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,11 @@ namespace MCGalaxy {
|
||||
public bool Files, Database, Lite;
|
||||
}
|
||||
|
||||
public static void CreatePackage(Player p, bool files, bool db, bool lite) {
|
||||
public static void Perform(Player p, bool files, bool db, bool lite, bool compress) {
|
||||
if (db) {
|
||||
Logger.Log(LogType.SystemActivity, "Backing up the database...");
|
||||
using (StreamWriter sql = new StreamWriter(sqlPath))
|
||||
BackupDatabase(sql,lite);
|
||||
BackupDatabase(sql);
|
||||
Logger.Log(LogType.SystemActivity, "Backed up the database to " + sqlPath);
|
||||
}
|
||||
|
||||
@ -46,13 +46,9 @@ namespace MCGalaxy {
|
||||
|
||||
Logger.Log(LogType.SystemActivity, "Creating compressed backup...");
|
||||
using (Stream stream = File.Create(zipPath)) {
|
||||
ZipWriter writer = new ZipWriter(stream);
|
||||
if (files) {
|
||||
Logger.Log(LogType.SystemActivity, "Compressing files...");
|
||||
SaveFiles(writer, filesList);
|
||||
}
|
||||
|
||||
if (db) SaveDatabase(writer);
|
||||
ZipWriter writer = new ZipWriter(stream);
|
||||
if (files) SaveFiles(writer, filesList, compress);
|
||||
if (db) SaveDatabase(writer, compress);
|
||||
|
||||
writer.FinishEntries();
|
||||
writer.WriteFooter();
|
||||
@ -87,23 +83,29 @@ namespace MCGalaxy {
|
||||
return paths;
|
||||
}
|
||||
|
||||
static void SaveFiles(ZipWriter writer, List<string> paths) {
|
||||
foreach (string path in paths) {
|
||||
static void SaveFiles(ZipWriter writer, List<string> paths, bool compress) {
|
||||
Logger.Log(LogType.SystemActivity, "Compressing {0} files...", paths.Count);
|
||||
for (int i = 0; i < paths.Count; i++) {
|
||||
string path = paths[i];
|
||||
bool compressThis = compress && !path.CaselessContains(".lvl");
|
||||
|
||||
try {
|
||||
using (Stream src = File.OpenRead(path)) {
|
||||
writer.WriteEntry(src, path);
|
||||
writer.WriteEntry(src, path, compressThis);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.LogError("Failed to backup file: " + path, ex);
|
||||
}
|
||||
|
||||
if (i == 0 || (i % 100) != 0) continue;
|
||||
Logger.Log(LogType.SystemActivity, "Backed up {0}/{1} files", i, paths.Count);
|
||||
}
|
||||
}
|
||||
|
||||
static void SaveDatabase(ZipWriter writer) {
|
||||
static void SaveDatabase(ZipWriter writer, bool compress) {
|
||||
Logger.Log(LogType.SystemActivity, "Compressing Database...");
|
||||
// TODO: gzip compress
|
||||
using (FileStream fs = File.OpenRead(sqlPath)) {
|
||||
writer.WriteEntry(fs, sqlPath);
|
||||
writer.WriteEntry(fs, sqlPath, compress);
|
||||
}
|
||||
Logger.Log(LogType.SystemActivity, "Database compressed");
|
||||
}
|
||||
@ -123,7 +125,7 @@ namespace MCGalaxy {
|
||||
}
|
||||
}
|
||||
|
||||
// To make life easier, we reload settings now, to maker it less likely to need restart
|
||||
// To make life easier, we reload settings now, to make it less likely to need restart
|
||||
Server.LoadAllSettings();
|
||||
p.Message("Server restored" + (errors > 0 ? " with errors. May be a partial restore" : ""));
|
||||
p.Message("It is recommended that you restart the server, although this is not required.");
|
||||
|
@ -24,13 +24,8 @@ using MCGalaxy.SQL;
|
||||
namespace MCGalaxy {
|
||||
public static partial class Backup {
|
||||
|
||||
public static void BackupDatabase(StreamWriter sql, bool lite) {
|
||||
//We technically know all tables in the DB... But since this is MySQL, we can also get them all with a MySQL command
|
||||
//So we show the tables, and store the result.
|
||||
//Also output information data (Same format as phpMyAdmin's dump)
|
||||
|
||||
//Important note: This does NOT account for foreign keys, BLOB's etc. It only works for what we actually put in the db.
|
||||
|
||||
public static void BackupDatabase(StreamWriter sql) {
|
||||
// NOTE: This does NOT account for foreign keys, BLOBs etc. It only works for what we actually put in the DB.
|
||||
sql.WriteLine("-- {0} SQL Database Dump", Server.SoftwareName);
|
||||
sql.WriteLine("-- Host: {0}", ServerConfig.MySQLHost);
|
||||
sql.WriteLine("-- Generation Time: {0:d} at {0:HH:mm:ss}", DateTime.Now);
|
||||
@ -40,7 +35,6 @@ namespace MCGalaxy {
|
||||
|
||||
List<string> tables = Database.Backend.AllTables();
|
||||
foreach (string name in tables) {
|
||||
if (lite && name.CaselessStarts("Block")) continue;
|
||||
BackupTable(name, sql);
|
||||
}
|
||||
}
|
||||
@ -60,18 +54,17 @@ namespace MCGalaxy {
|
||||
|
||||
internal static void ReplaceDatabase(Stream sql) {
|
||||
using (FileStream backup = File.Create("backup.sql"))
|
||||
BackupDatabase(new StreamWriter(backup), false); // backup
|
||||
BackupDatabase(new StreamWriter(backup));
|
||||
|
||||
List<string> tables = Database.Backend.AllTables();
|
||||
foreach (string table in tables)
|
||||
Database.Backend.DeleteTable(table); // drop all tables
|
||||
Database.Backend.DeleteTable(table);
|
||||
|
||||
ImportSql(sql);
|
||||
}
|
||||
|
||||
internal static void ImportSql(Stream sql) {
|
||||
// Import data (we only have CREATE TABLE and INSERT INTO statements)
|
||||
|
||||
// Import data (we only have CREATE TABLE and INSERT INTO statements)
|
||||
using (StreamReader reader = new StreamReader(sql)) {
|
||||
ImportBulk(reader);
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace MCGalaxy {
|
||||
|
||||
struct ZipEntry {
|
||||
public byte[] Filename;
|
||||
public byte[] Filename;
|
||||
public long CompressedSize, UncompressedSize, LocalHeaderOffset;
|
||||
public uint Crc32;
|
||||
public ushort BitFlags, CompressionMethod;
|
||||
@ -37,15 +38,11 @@ namespace MCGalaxy {
|
||||
}
|
||||
|
||||
sealed class ZipEntryStream : Stream {
|
||||
public uint Crc32;
|
||||
public uint Crc32 = uint.MaxValue;
|
||||
public long CompressedLen;
|
||||
Stream stream;
|
||||
|
||||
public ZipEntryStream(Stream stream) {
|
||||
this.stream = stream;
|
||||
Crc32 = uint.MaxValue;
|
||||
}
|
||||
public Stream stream;
|
||||
|
||||
public ZipEntryStream(Stream stream) { this.stream = stream; }
|
||||
public override bool CanRead { get { return false; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
@ -61,21 +58,35 @@ namespace MCGalaxy {
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
stream.Write(buffer, offset, count);
|
||||
CompressedLen += count;
|
||||
|
||||
for (int i = offset; i < offset + count; i++) {
|
||||
Crc32 = crc32Table[(Crc32 ^ buffer[i]) & 0xFF] ^ (Crc32 >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value) {
|
||||
stream.WriteByte(value);
|
||||
CompressedLen++;
|
||||
Crc32 = crc32Table[(Crc32 ^ value) & 0xFF] ^ (Crc32 >> 8);
|
||||
}
|
||||
|
||||
public override void Close() {
|
||||
stream = null;
|
||||
Crc32 ^= uint.MaxValue;
|
||||
public override void Close() { stream = null; }
|
||||
public long WriteStream(Stream src, byte[] buffer, bool compress) {
|
||||
if (compress) {
|
||||
using (DeflateStream ds = new DeflateStream(this, CompressionMode.Compress))
|
||||
return WriteData(ds, src, buffer);
|
||||
}
|
||||
return WriteData(this, src, buffer);
|
||||
}
|
||||
|
||||
long WriteData(Stream dst, Stream src, byte[] buffer) {
|
||||
int count = 0;
|
||||
long totalLen = 0;
|
||||
|
||||
while ((count = src.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
dst.Write(buffer, 0, count);
|
||||
totalLen += count;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Crc32 = crc32Table[(Crc32 ^ buffer[i]) & 0xFF] ^ (Crc32 >> 8);
|
||||
}
|
||||
}
|
||||
return totalLen;
|
||||
}
|
||||
|
||||
static uint[] crc32Table;
|
||||
@ -93,7 +104,7 @@ namespace MCGalaxy {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class ZipWriter {
|
||||
BinaryWriter w;
|
||||
Stream stream;
|
||||
@ -112,7 +123,7 @@ namespace MCGalaxy {
|
||||
w = new BinaryWriter(stream);
|
||||
}
|
||||
|
||||
public void WriteEntry(Stream src, string file) {
|
||||
public void WriteEntry(Stream src, string file, bool compress) {
|
||||
ZipEntry entry = default(ZipEntry);
|
||||
entry.Filename = Encoding.UTF8.GetBytes(file);
|
||||
entry.LocalHeaderOffset = stream.Position;
|
||||
@ -121,23 +132,18 @@ namespace MCGalaxy {
|
||||
int headerSize = 30 + entry.Filename.Length + zip64Extra;
|
||||
stream.Write(buffer, 0, headerSize);
|
||||
|
||||
// bit flag for non-ascii filename
|
||||
// set bit flag for non-ascii filename
|
||||
foreach (char c in file) {
|
||||
if (c < ' ' || c > '~') entry.BitFlags |= (1 << 11);
|
||||
}
|
||||
|
||||
ZipEntryStream dst;
|
||||
using (dst = new ZipEntryStream(stream)) {
|
||||
int read = 0;
|
||||
|
||||
while ((read = src.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
dst.Write(buffer, 0, read);
|
||||
entry.UncompressedSize += read;
|
||||
}
|
||||
}
|
||||
if (compress) entry.CompressionMethod = 8;
|
||||
ZipEntryStream dst = new ZipEntryStream(stream);
|
||||
entry.UncompressedSize = dst.WriteStream(src, buffer, compress);
|
||||
dst.stream = null;
|
||||
|
||||
entry.CompressedSize = dst.CompressedLen;
|
||||
entry.Crc32 = dst.Crc32;
|
||||
entry.Crc32 = dst.Crc32 ^ uint.MaxValue;
|
||||
entries.Add(entry); numEntries++;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user