mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-04-20 02:56:44 +00:00
A different implementation of a zip based file management system.
Changes to the way save.json files are handled. They are now wrapped in a zip and deleted at certain intervals based on config.properties values.
This commit is contained in:
parent
f08b5632cc
commit
104b6ebde8
192
Server/src/main/java/net/simon987/server/FileUtils.java
Normal file
192
Server/src/main/java/net/simon987/server/FileUtils.java
Normal file
@ -0,0 +1,192 @@
|
||||
package net.simon987.server;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
private static final String STR_ENCODING = "UTF-8";
|
||||
private static final String DATE_FORMAT = "yyyyMMddHHmmss";
|
||||
private static final String FILE_TYPE = ".zip";
|
||||
private static final Path ROOT_DIR;
|
||||
private static final String DIR_NAME = "history";
|
||||
public static final Path DIR_PATH;
|
||||
|
||||
static {
|
||||
ROOT_DIR = Paths.get(".").normalize();
|
||||
DIR_PATH = ROOT_DIR.resolve(DIR_NAME);
|
||||
}
|
||||
|
||||
//Private constructor
|
||||
private FileUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new stamp containing the current date and time
|
||||
*
|
||||
* @return date and time stamp
|
||||
*/
|
||||
private static String getDateTimeStamp() {
|
||||
Date millisToDate = new Date(System.currentTimeMillis());
|
||||
SimpleDateFormat f = new SimpleDateFormat(DATE_FORMAT);
|
||||
return f.format(millisToDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Created a directory if none exists with the specified name
|
||||
*
|
||||
* @param name folder to create
|
||||
* @return true is the file exists or create operation is successful
|
||||
*/
|
||||
public static boolean prepDirectory(Path directory) {
|
||||
File file = directory.toFile();
|
||||
|
||||
//If the directory exists or the directory created successfully return true
|
||||
if(file.exists() || file.mkdir()) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
System.out.println("Error creating directory: " + file.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a file into an array of bytes
|
||||
*
|
||||
* @param fileName the file to be converted into bytes
|
||||
* @return the byte array of the given file
|
||||
*/
|
||||
public static byte[] bytifyFile(Path path) {
|
||||
byte[] bytes = null;
|
||||
|
||||
try {
|
||||
bytes = Files.readAllBytes(path);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to extract bytes from: " + path);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a file that had been converted to a byte[] to be written to a new
|
||||
* zip file
|
||||
*
|
||||
* @param payload
|
||||
* contains data in byte array form to be written, typically a file
|
||||
* that has been converted with bytifyFile()
|
||||
* @throws IOException
|
||||
* if an error occurs during the write process
|
||||
*/
|
||||
public static void writeSaveToZip(String name, byte[] data) throws IOException {
|
||||
|
||||
String newFile = DIR_PATH.resolve(getDateTimeStamp() + FILE_TYPE).toString();
|
||||
FileOutputStream output = new FileOutputStream(newFile);
|
||||
ZipOutputStream stream = new ZipOutputStream(output);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
|
||||
|
||||
while ((bais.read(buffer)) > -1) {
|
||||
// File name
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
// Set to start of next entry in the stream.
|
||||
stream.putNextEntry(entry);
|
||||
// Data to write.
|
||||
stream.write(data);
|
||||
// Close the current entry.
|
||||
stream.closeEntry();
|
||||
}
|
||||
|
||||
stream.close();
|
||||
output.close();
|
||||
}
|
||||
|
||||
public static void cleanHistory(int size) {
|
||||
|
||||
|
||||
File[] files = new File(DIR_PATH.toString()).listFiles();
|
||||
File[] sorted = new File[size];
|
||||
|
||||
File nextSortedFile = null;
|
||||
File currentFile = null;
|
||||
boolean changed = false;
|
||||
|
||||
for(int i = 0; i < files.length / 2; i++) {
|
||||
currentFile = files[i];
|
||||
files[i] = files[files.length - i - 1];
|
||||
files[files.length - i - 1] = currentFile;
|
||||
}
|
||||
|
||||
currentFile = null;
|
||||
|
||||
for(int f = 0; f < files.length; f++) {
|
||||
changed = false;
|
||||
long dirFile = Long.parseLong(files[f].getName().substring(0, (files[f].getName().length() -4)));
|
||||
|
||||
if(f < size && sorted[f] == null) {
|
||||
sorted[f] = files[f];
|
||||
|
||||
} else {
|
||||
|
||||
for(int s = 0; s < sorted.length; s++) {
|
||||
|
||||
long sortedFile = Long.parseLong(sorted[s].getName().substring(0, (sorted[s].getName().length() -4)));
|
||||
|
||||
if(dirFile > sortedFile) {
|
||||
|
||||
if(s == sorted.length - 1) {
|
||||
sorted[s] = files[f];
|
||||
|
||||
} else if(nextSortedFile == null) {
|
||||
nextSortedFile = sorted[s];
|
||||
sorted[s] = files[f];
|
||||
|
||||
} else {
|
||||
currentFile = sorted[s];
|
||||
sorted[s] = nextSortedFile;
|
||||
nextSortedFile = currentFile;
|
||||
}
|
||||
|
||||
nextSortedFile = null;
|
||||
currentFile = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(changed == false) {
|
||||
files[f].delete();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array into human readable format using the provided encoding
|
||||
*
|
||||
* @param bytes
|
||||
* data to be encoded to String
|
||||
* @return a String containing the encoded bytes
|
||||
*/
|
||||
public static String byteArrAsString(byte[] bytes) throws UnsupportedEncodingException {
|
||||
return new String(bytes, STR_ENCODING);
|
||||
}
|
||||
}
|
@ -16,11 +16,13 @@ import org.json.simple.JSONObject;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GameServer implements Runnable {
|
||||
|
||||
public final static GameServer INSTANCE = new GameServer();
|
||||
private static final String SAVE_JSON = "save.json";
|
||||
|
||||
private GameUniverse gameUniverse;
|
||||
private GameEventDispatcher eventDispatcher;
|
||||
@ -32,10 +34,6 @@ public class GameServer implements Runnable {
|
||||
|
||||
private int maxExecutionTime;
|
||||
|
||||
public ArrayList<byte[]> saveArchive;
|
||||
|
||||
public int maxArchiveSize;
|
||||
|
||||
public GameServer() {
|
||||
|
||||
this.config = new ServerConfiguration(new File("config.properties"));
|
||||
@ -64,10 +62,6 @@ public class GameServer implements Runnable {
|
||||
}
|
||||
|
||||
eventDispatcher = new GameEventDispatcher(pluginManager);
|
||||
|
||||
saveArchive = new ArrayList<byte[]>();
|
||||
|
||||
maxArchiveSize = config.getInt("max_archive_size");
|
||||
}
|
||||
|
||||
public GameUniverse getGameUniverse() {
|
||||
@ -149,7 +143,12 @@ public class GameServer implements Runnable {
|
||||
|
||||
// Save
|
||||
if (gameUniverse.getTime() % config.getInt("save_interval") == 0) {
|
||||
save(new File("save.json"));
|
||||
save(new File(SAVE_JSON));
|
||||
}
|
||||
|
||||
// Clean up history files
|
||||
if(gameUniverse.getTime() % config.getInt("clean_interval") == 0) {
|
||||
FileUtils.cleanHistory(config.getInt("history_size"));
|
||||
}
|
||||
|
||||
socketServer.tick();
|
||||
@ -165,10 +164,15 @@ public class GameServer implements Runnable {
|
||||
*/
|
||||
public void save(File file) {
|
||||
|
||||
if (new File(new File("save.json").getAbsolutePath()).exists()) {
|
||||
saveArchive.add(ZipUtils.bytifyFile("save.json"));
|
||||
while(saveArchive.size() > maxArchiveSize) {
|
||||
saveArchive.remove(0);
|
||||
boolean dirExists = FileUtils.prepDirectory(FileUtils.DIR_PATH);
|
||||
|
||||
if (new File(new File(SAVE_JSON).getAbsolutePath()).exists() && dirExists) {
|
||||
byte[] data = FileUtils.bytifyFile(new File(SAVE_JSON).toPath());
|
||||
try {
|
||||
FileUtils.writeSaveToZip(SAVE_JSON, data);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to write " + SAVE_JSON + " to zip file");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,8 +211,4 @@ public class GameServer implements Runnable {
|
||||
public void setSocketServer(SocketServer socketServer) {
|
||||
this.socketServer = socketServer;
|
||||
}
|
||||
|
||||
public ArrayList<byte[]> getSaveArchive() {
|
||||
return this.saveArchive;
|
||||
}
|
||||
}
|
||||
|
@ -11,18 +11,6 @@ import java.net.InetSocketAddress;
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
|
||||
//Writes all of the files stored in GameServer.saveArray to a zip file.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
ZipUtils.writeSavesToZip(GameServer.INSTANCE.getSaveArchive());
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error writing saves to zip");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "Shutdown-thread"));
|
||||
|
||||
LogManager.initialize();
|
||||
ServerConfiguration config = new ServerConfiguration(new File("config.properties"));
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
package net.simon987.server;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.simon987.server.logging.LogManager;
|
||||
|
||||
public class ZipUtils {
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
public static byte[] bytifyFile(String fileName) {
|
||||
|
||||
Path path = Paths.get(fileName);
|
||||
byte[] bytes = null;
|
||||
|
||||
try {
|
||||
bytes = Files.readAllBytes(path);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to extract bytes from: " + fileName);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String getByteArrAsString(byte[] bytes) throws UnsupportedEncodingException {
|
||||
return new String(bytes, "UTF-8");
|
||||
}
|
||||
|
||||
public static void writeSavesToZip(ArrayList<byte[]> array) throws IOException {
|
||||
|
||||
int writeCount = 0;
|
||||
FileOutputStream output = new FileOutputStream("archive_" + getDateTimeStamp() + ".zip");
|
||||
ZipOutputStream stream = new ZipOutputStream(output);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
|
||||
|
||||
while ((bais.read(buffer)) > -1) {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
|
||||
ZipEntry entry = new ZipEntry("save_" + getTickTime(array.get(i)) + ".json");
|
||||
stream.putNextEntry(entry);
|
||||
stream.write(array.get(i));
|
||||
stream.closeEntry();
|
||||
writeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
stream.close();
|
||||
output.close();
|
||||
|
||||
LogManager.LOGGER.info(writeCount + " saves moved to zip file archive");
|
||||
}
|
||||
|
||||
private static String getTickTime(byte[] bytes) throws UnsupportedEncodingException {
|
||||
|
||||
Pattern pattern = Pattern.compile("\"time\"");
|
||||
String stringedBytes = getByteArrAsString(bytes);
|
||||
Matcher matcher = pattern.matcher(stringedBytes);
|
||||
int startIndex = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
startIndex = matcher.end() + 1;
|
||||
}
|
||||
|
||||
int endIndex = stringedBytes.indexOf(",", startIndex);
|
||||
|
||||
return stringedBytes.substring(startIndex, endIndex);
|
||||
}
|
||||
|
||||
private static String getDateTimeStamp() {
|
||||
Date millisToDate = new Date(System.currentTimeMillis());
|
||||
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
return f.format(millisToDate);
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,9 @@ mysql_pass=mar
|
||||
# MySQL address
|
||||
mysql_url=jdbc:mysql://localhost:3306/mar?useSSL=false
|
||||
save_interval=10
|
||||
# File management
|
||||
clean_interval=10
|
||||
history_size=10
|
||||
# Web server port
|
||||
webSocket_port=8887
|
||||
webSocket_host=0.0.0.0
|
||||
@ -63,6 +66,3 @@ wg_maxCopperCount=2
|
||||
user_timeout=500
|
||||
# Free CPU execution time in ms
|
||||
user_free_execution_time=2
|
||||
# ----------------------------------------------
|
||||
# Max saves to archive when the server is shutdown
|
||||
max_archive_size=10
|
Loading…
x
Reference in New Issue
Block a user