Skip to content

Commit 922bcbc

Browse files
committed
0x01
1 parent 14ca3be commit 922bcbc

File tree

12 files changed

+249
-86
lines changed

12 files changed

+249
-86
lines changed

README.md

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,17 @@
1-
# Example Mod
1+
# QuietSave
22

3-
Template for making Babric mods for BTA!
3+
A world backup plugin for BTA servers.
44

5-
**Note: *DO NOT fork this repository unless you want to contribute!***
5+
This plugin will automatically zip your server's world into a specified directory every so often (20 minutes by default)
66

7-
## Prerequisites
8-
- JDK for Java 17 ([Eclipse Temurin](https://adoptium.net/temurin/releases/) recommended)
9-
- [Intellij IDEA](https://www.jetbrains.com/idea/download/) (Scroll down for the free community edition, if using linux **DO NOT** use the flatpak distribution)
10-
- Minecraft Development plugin (Optional, but highly recommended)
7+
You can also manually backup the world.
118

12-
## Setup instructions
13-
9+
### Config options
1410

15-
1. Click the `Use this template` button on this repo's page above (Will only appear if logged in). Choose `Create a new repository`, you will be redirected to a new page. Enter your repo's name and description, and hit `Create repository`.
16-
To get your project, open IntelliJ IDEA and click `Get from VCS`. Select `Repository URL` and enter your repo's url
11+
- `saveFrequency`: How often backups are saved, in minutes.
1712

18-
2. After the project has finished importing, close it and open it again.
19-
If that does not work, open the right sidebar with `Gradle` on it, open `Tasks` > `fabric` and run `ideaSyncTask`.
13+
- `saveDir`: Directory in the root of the minecraft server to save backups to. This will be created if it does not exist.
2014

21-
3. Create a new run configuration by going in `Run > Edit Configurations`.
22-
Then click on the plus icon and select Gradle. In the `Tasks and Arguments` field enter `build`.
23-
Running it will build your finished jar files and put them in `build/libs/`.
24-
25-
4. Lastly, open `File` > `Settings` and head to `Build, Execution, Development` > `Build Tools` > `Gradle`.
26-
Make sure `Build and run using` and `Run tests using` is set to `Gradle`.
27-
28-
5. Done! Now, all that's left is to change every mention of `examplemod` and `turniplabs` to your own mod id and mod group, respectively. Happy modding!
29-
30-
## Tips
31-
32-
1. If you haven't already you should join the BTA modding discord! https://discord.gg/FTUNJhswBT
33-
2. You can set your username when launching the client run configuration by setting `--username <username>` in your program arguments.
34-
3. When launching the server run configuration you may want to remove the `nogui` program argument in order to see the regular server GUI.
35-
4. In Intellij you can double press shift or press ctrl+N to search class files, change the search from the default `Project Files` to `All Places` you can easily explore the classes for you dependencies and even BTA itself.
36-
5. In Intellij if ctrl+left click on a field or method you can quickly get information on when and where that field or method is assign or used.
15+
### Commands
3716

17+
- `backup`: Saves the current world to the backups directory

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ halplibe_version=3.5.2
1212

1313
# Mod
1414
mod_version=1.0.0
15-
mod_group=turniplabs
16-
mod_name=examplemod
15+
mod_group=wyspr
16+
mod_name=quietsave

src/main/java/turniplabs/examplemod/ExampleMod.java

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package wyspr.quietsave;
2+
3+
import net.minecraft.server.MinecraftServer;
4+
import net.minecraft.server.world.WorldServer;
5+
6+
import java.io.File;
7+
import java.io.IOException;
8+
import java.nio.file.*;
9+
import java.nio.file.attribute.BasicFileAttributes;
10+
import java.text.SimpleDateFormat;
11+
import java.util.Date;
12+
import java.util.zip.ZipEntry;
13+
import java.util.zip.ZipOutputStream;
14+
15+
public class BackupManager {
16+
private long lastBackupTime = System.currentTimeMillis();
17+
18+
public static boolean backup() {
19+
MinecraftServer mc = MinecraftServer.getInstance();
20+
21+
for (WorldServer world : mc.dimensionWorlds) {
22+
world.saveWorld(true, null, true);
23+
}
24+
25+
String worldName = mc.propertyManager.getStringProperty("level-name", "world");
26+
Path worldPath = Paths.get(worldName);
27+
28+
Date now = new Date();
29+
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd - HH.mm");
30+
String formattedDate = formatter.format(now);
31+
File zipFileName = QuietSave.SAVE_DIR.resolve(formattedDate + ".zip").toFile();
32+
33+
try {
34+
final ZipOutputStream outputStream = new ZipOutputStream(Files.newOutputStream(zipFileName.toPath()));
35+
Files.walkFileTree(worldPath, new SimpleFileVisitor<Path>() {
36+
@Override
37+
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
38+
try {
39+
Path targetFile = worldPath.relativize(file);
40+
outputStream.putNextEntry(new ZipEntry(targetFile.toString()));
41+
byte[] bytes = Files.readAllBytes(file);
42+
outputStream.write(bytes, 0, bytes.length);
43+
outputStream.closeEntry();
44+
} catch (IOException e) {
45+
QuietSave.LOGGER.error("Failed to zip files", e);
46+
}
47+
return FileVisitResult.CONTINUE;
48+
}
49+
});
50+
outputStream.close();
51+
} catch (IOException e) {
52+
QuietSave.LOGGER.error("Failed to zip files", e);
53+
return false;
54+
}
55+
System.out.println("World backup saved to: " + zipFileName);
56+
QuietSave.LOGGER.info("World backup saved to: {}", zipFileName);
57+
return true;
58+
}
59+
60+
public void tick() {
61+
long now = System.currentTimeMillis();
62+
long timeChange = now - this.lastBackupTime;
63+
64+
if (timeChange >= QuietSave.SAVE_FREQ * 60000) {
65+
boolean backupSucessful = backup();
66+
if (backupSucessful) {
67+
this.lastBackupTime = now;
68+
}
69+
}
70+
}
71+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package wyspr.quietsave;
2+
3+
import net.fabricmc.api.ModInitializer;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import turniplabs.halplibe.util.GameStartEntrypoint;
7+
import turniplabs.halplibe.util.RecipeEntrypoint;
8+
import turniplabs.halplibe.util.TomlConfigHandler;
9+
import turniplabs.halplibe.util.toml.Toml;
10+
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.Paths;
15+
16+
17+
public class QuietSave implements ModInitializer, GameStartEntrypoint, RecipeEntrypoint {
18+
public static final String MOD_ID = "quietsave";
19+
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
20+
21+
public static final TomlConfigHandler CONFIG;
22+
public static Path SAVE_DIR;
23+
public static long SAVE_FREQ;
24+
public static BackupManager BACKUPS;
25+
26+
static {
27+
Toml toml = new Toml();
28+
toml.addEntry("saveFrequency", "How often backups are saved (in minutes)", 20)
29+
.addEntry("saveDir", "Directory to save backups to", "backups");
30+
31+
CONFIG = new TomlConfigHandler(MOD_ID, toml);
32+
33+
SAVE_FREQ = CONFIG.getInt("saveFrequency");
34+
SAVE_DIR = Paths.get(CONFIG.getString("saveDir"));
35+
}
36+
37+
@Override
38+
public void onInitialize() {
39+
LOGGER.info("QuietSave initialized.");
40+
Path saveDirPath = SAVE_DIR;
41+
if (!Files.exists(saveDirPath)) {
42+
try {
43+
Files.createDirectories(saveDirPath);
44+
System.out.println("Created /" + saveDirPath +"/ for world backups");
45+
} catch (IOException e) {
46+
System.out.println("Could not create save directory: " + SAVE_DIR);
47+
return;
48+
}
49+
}
50+
BACKUPS = new BackupManager();
51+
}
52+
53+
@Override
54+
public void beforeGameStart() {
55+
56+
}
57+
58+
@Override
59+
public void afterGameStart() {
60+
}
61+
62+
@Override
63+
public void onRecipesReady() {
64+
65+
}
66+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package wyspr.quietsave.commands;
2+
3+
import net.minecraft.core.net.command.Command;
4+
import net.minecraft.core.net.command.CommandHandler;
5+
import net.minecraft.core.net.command.CommandSender;
6+
import wyspr.quietsave.BackupManager;
7+
8+
public class BackupCommand extends Command {
9+
public BackupCommand() {
10+
super("backup");
11+
}
12+
13+
@Override
14+
public boolean execute(CommandHandler handler, CommandSender sender, String[] args) {
15+
boolean backupFailed = !BackupManager.backup();
16+
17+
if (backupFailed) {
18+
sender.sendMessage("§e§lBackup failed for some reason, check the console!");
19+
} else {
20+
sender.sendMessage("§1§nBackup successful!");
21+
}
22+
23+
return true;
24+
}
25+
26+
@Override
27+
public boolean opRequired(String[] strings) {
28+
return true;
29+
}
30+
31+
@Override
32+
public void sendCommandSyntax(CommandHandler handler, CommandSender sender) {
33+
sender.sendMessage("§3/backup");
34+
sender.sendMessage("§5Backup the world data.");
35+
}
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package wyspr.quietsave.mixins;
2+
3+
import net.minecraft.core.net.command.Command;
4+
import net.minecraft.core.net.command.Commands;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.Shadow;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import org.spongepowered.asm.mixin.injection.Inject;
9+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10+
import wyspr.quietsave.commands.BackupCommand;
11+
12+
import java.util.List;
13+
14+
@Mixin(value = Commands.class, remap = false)
15+
public class CommandsMixin {
16+
@Shadow
17+
public static List<Command> commands;
18+
@Inject(method = "initCommands", at = @At("TAIL"))
19+
private static void addCommands(CallbackInfo ci) {
20+
commands.add(new BackupCommand());
21+
}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package wyspr.quietsave.mixins;
2+
3+
import net.minecraft.server.MinecraftServer;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.injection.At;
6+
import org.spongepowered.asm.mixin.injection.Inject;
7+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
8+
import wyspr.quietsave.QuietSave;
9+
10+
@Mixin(value = MinecraftServer.class, remap = false)
11+
public class MinecraftServerMixin {
12+
@Inject(method = "run", at = @At(value = "INVOKE",
13+
target = "Lnet/minecraft/server/MinecraftServer;doTick()V",
14+
shift = At.Shift.AFTER
15+
))
16+
public void checkTime(CallbackInfo ci) {
17+
QuietSave.BACKUPS.tick();
18+
}
19+
}

src/main/resources/examplemod.mixins.json

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/main/resources/fabric.mod.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"schemaVersion": 1,
3-
"id": "examplemod",
3+
"id": "quietsave",
44
"version": "${version}",
55

6-
"name": "Example Mod",
7-
"description": "This mod aims to help new BTA modders.",
6+
"name": "QuietSave",
7+
"description": "A world backup plugin for BTA servers.",
88
"authors": [
9-
"Turnip Labs"
9+
"wyspr"
1010
],
1111
"contact": {
1212
"homepage": "",
@@ -19,20 +19,20 @@
1919
"environment": "*",
2020
"entrypoints": {
2121
"main": [
22-
"turniplabs.examplemod.ExampleMod"
22+
"wyspr.quietsave.QuietSave"
2323
],
2424
"beforeGameStart": [
25-
"turniplabs.examplemod.ExampleMod"
25+
"wyspr.quietsave.QuietSave"
2626
],
2727
"afterGameStart": [
28-
"turniplabs.examplemod.ExampleMod"
28+
"wyspr.quietsave.QuietSave"
2929
],
3030
"recipesReady": [
31-
"turniplabs.examplemod.ExampleMod"
31+
"wyspr.quietsave.QuietSave"
3232
]
3333
},
3434
"mixins": [
35-
"examplemod.mixins.json"
35+
"quietsave.mixins.json"
3636
],
3737

3838
"depends": {

0 commit comments

Comments
 (0)