diff options
10 files changed, 158 insertions, 4 deletions
@@ -67,7 +67,6 @@ done ## 3. To-Do List: -- Op login hint in the next start after restoring - A more friendly help menu (colored command help menu) - New version checker - Code refactor for maintainability diff --git a/gradle.properties b/gradle.properties index de9a8ce..46963f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.14.4 yarn_mappings=1.14.4+build.18 loader_version=0.11.0 # Mod Properties -mod_version=1.6.2 +mod_version=1.6.3 maven_group=com.keuin.kbackupfabric archives_base_name=kbackup-fabric # Dependencies diff --git a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java index 8992d17..a8c3e0c 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java +++ b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java @@ -2,8 +2,11 @@ package com.keuin.kbackupfabric; import com.keuin.kbackupfabric.backup.BackupFilesystemUtil; import com.keuin.kbackupfabric.backup.suggestion.BackupNameSuggestionProvider; +import com.keuin.kbackupfabric.event.OnPlayerConnect; import com.keuin.kbackupfabric.metadata.BackupMetadata; import com.keuin.kbackupfabric.metadata.MetadataHolder; +import com.keuin.kbackupfabric.notification.DistinctNotifiable; +import com.keuin.kbackupfabric.notification.NotificationManager; import com.keuin.kbackupfabric.ui.KBCommands; import com.keuin.kbackupfabric.util.DateUtil; import com.keuin.kbackupfabric.util.PrintUtil; @@ -41,6 +44,11 @@ public final class KBPluginEvents implements ModInitializer, ServerStartCallback if (!(server instanceof MinecraftDedicatedServer)) throw new RuntimeException("KBackup is a server-side-only plugin. Please do not use it in client-side."); + // Bind fabric events + + OnPlayerConnect.ON_PLAYER_CONNECT.register((connection, player) + -> NotificationManager.INSTANCE.notifyPlayer(DistinctNotifiable.fromServerPlayerEntity(player))); + // Initialize player manager reference PrintUtil.setPlayerManager(server.getPlayerManager()); diff --git a/src/main/java/com/keuin/kbackupfabric/event/OnPlayerConnect.java b/src/main/java/com/keuin/kbackupfabric/event/OnPlayerConnect.java new file mode 100644 index 0000000..490249b --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/event/OnPlayerConnect.java @@ -0,0 +1,18 @@ +package com.keuin.kbackupfabric.event; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.network.ClientConnection; +import net.minecraft.server.network.ServerPlayerEntity; + +public class OnPlayerConnect { + public interface PlayerConnectEventCallback { + void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player); + } + + public static final Event<PlayerConnectEventCallback> ON_PLAYER_CONNECT = EventFactory.createArrayBacked(PlayerConnectEventCallback.class, callbacks -> (conn, player) -> { + for (PlayerConnectEventCallback callback : callbacks) { + callback.onPlayerConnect(conn, player); + } + }); +} diff --git a/src/main/java/com/keuin/kbackupfabric/mixin/PlayerManagerMixin.java b/src/main/java/com/keuin/kbackupfabric/mixin/PlayerManagerMixin.java new file mode 100644 index 0000000..eab87e1 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/mixin/PlayerManagerMixin.java @@ -0,0 +1,18 @@ +package com.keuin.kbackupfabric.mixin; + +import com.keuin.kbackupfabric.event.OnPlayerConnect; +import net.minecraft.network.ClientConnection; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerManager.class) +public class PlayerManagerMixin { + @Inject(method = "onPlayerConnect", at = @At("TAIL")) + public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { + OnPlayerConnect.ON_PLAYER_CONNECT.invoker().onPlayerConnect(connection, player); + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/notification/DistinctNotifiable.java b/src/main/java/com/keuin/kbackupfabric/notification/DistinctNotifiable.java new file mode 100644 index 0000000..b44bfce --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/notification/DistinctNotifiable.java @@ -0,0 +1,45 @@ +package com.keuin.kbackupfabric.notification; + +import net.minecraft.network.MessageType; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +/** + * Decouple from ServerPlayerEntity, in case further migration to other APIs. + */ +public interface DistinctNotifiable { + + /** + * Does the receiver has privilege to receive some special message. + */ + boolean isPrivileged(); + + void notify(Text text); + + /** + * Get an unique, non-null object that identifies this notifiable instance. + * The identifier must be immutable and implement their own equals method. + * + * @return the identifier. + */ + Object getIdentifier(); + + static DistinctNotifiable fromServerPlayerEntity(ServerPlayerEntity serverPlayerEntity) { + return new DistinctNotifiable() { + @Override + public boolean isPrivileged() { + return serverPlayerEntity.server.getPermissionLevel(serverPlayerEntity.getGameProfile()) >= serverPlayerEntity.server.getOpPermissionLevel(); + } + + @Override + public void notify(Text text) { + serverPlayerEntity.sendChatMessage(text, MessageType.SYSTEM); + } + + @Override + public Object getIdentifier() { + return serverPlayerEntity.getUuid(); + } + }; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/notification/NotificationManager.java b/src/main/java/com/keuin/kbackupfabric/notification/NotificationManager.java new file mode 100644 index 0000000..96ecf41 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/notification/NotificationManager.java @@ -0,0 +1,49 @@ +package com.keuin.kbackupfabric.notification; + +import com.keuin.kbackupfabric.metadata.BackupMetadata; +import com.keuin.kbackupfabric.metadata.MetadataHolder; +import com.keuin.kbackupfabric.util.DateUtil; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Style; +import net.minecraft.util.Formatting; + +import java.util.HashSet; +import java.util.Set; + +/** + * Notify some users when the server has been restored to a backup. + */ +public class NotificationManager { + + public static final NotificationManager INSTANCE = new NotificationManager(); + + private final Set<Object> notified = new HashSet<>(); + + private NotificationManager() { + } + + public void notifyPlayer(DistinctNotifiable distinctNotifiable) { + Object identifier = distinctNotifiable.getIdentifier(); + if (distinctNotifiable.isPrivileged() && !notified.contains(identifier)) { + notified.add(identifier); + notify(distinctNotifiable); + } + } + + /** + * Just notify if necessary. It will not update the set. + */ + private void notify(DistinctNotifiable notifiable) { + if (MetadataHolder.hasMetadata()) { + BackupMetadata backup = MetadataHolder.getMetadata(); + notifiable.notify( + new LiteralText("The server has been restored to backup ") + .append(new LiteralText("[" + backup.getBackupName() + "]").setStyle(new Style().setColor(Formatting.GREEN))) + .append(new LiteralText(" (created at ")) + .append(new LiteralText("[" + DateUtil.fromEpochMillis(backup.getBackupTime()) + "]").setStyle(new Style().setColor(Formatting.GREEN))) + .append(new LiteralText(")")) + ); + } + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/ui/KBCommands.java b/src/main/java/com/keuin/kbackupfabric/ui/KBCommands.java index 87f667d..e148f24 100644 --- a/src/main/java/com/keuin/kbackupfabric/ui/KBCommands.java +++ b/src/main/java/com/keuin/kbackupfabric/ui/KBCommands.java @@ -288,7 +288,7 @@ public final class KBCommands { //KBMain.restore("name") MinecraftServer server = context.getSource().getMinecraftServer(); String backupFileName = parseBackupFileName(context, StringArgumentType.getString(context, "backupName")); - backupFileName = parseBackupFileName(context, backupFileName); +// backupFileName = parseBackupFileName(context, backupFileName); if (backupFileName == null) return list(context); // Show the list and return @@ -501,7 +501,7 @@ public final class KBCommands { String backupName = StringArgumentType.getString(context, "backupName"); if (backupName.matches("[0-9]*")) { - // If numeric input + // treat numeric input as backup index number in list int index = Integer.parseInt(backupName) - 1; synchronized (backupList) { return backupList.get(index).getBackupFileName(); // Replace input number with real backup file name. diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e2d9d39..5996e4b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,6 +20,7 @@ ] }, "mixins": [ + "kbackupfabric.mixins.json" ], "depends": { "fabricloader": ">=0.7.4", diff --git a/src/main/resources/kbackupfabric.mixins.json b/src/main/resources/kbackupfabric.mixins.json new file mode 100644 index 0000000..b31d0c7 --- /dev/null +++ b/src/main/resources/kbackupfabric.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "package": "com.keuin.kbackupfabric.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "PlayerManagerMixin" + ], + "client": [ + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "verbose": true +}
\ No newline at end of file |