diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java index 06cb6a578..f9f631df3 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java @@ -15,6 +15,7 @@ import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; @@ -115,10 +116,10 @@ public class AdminUnregisterCommand extends ConfirmableCommand { // Remove all island players that reference this island targetIsland.getMembers().clear(); if (user.isPlayer()) { - targetIsland.log(new LogEntry.Builder("UNREGISTER").data("player", targetUUID.toString()) + targetIsland.log(new LogEntry.Builder(LogType.UNREGISTER).data("player", targetUUID.toString()) .data("admin", user.getUniqueId().toString()).build()); } else { - targetIsland.log(new LogEntry.Builder("UNREGISTER").data("player", targetUUID.toString()) + targetIsland.log(new LogEntry.Builder(LogType.UNREGISTER).data("player", targetUUID.toString()) .data("admin", "console").build()); } user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(targetIsland.getCenter().toVector()), diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 9244508c7..f0e3523bd 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -9,6 +9,8 @@ import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.team.TeamEvent; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.TeamInvite; @@ -120,6 +122,9 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName()); } + // Add historu record + island.log(new LogEntry.Builder(LogType.TRUSTED).data(user.getUniqueId().toString(), "trusted") + .data(invite.getInviter().toString(), "trusted by").build()); } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java index 16e23ee5c..0533f65ea 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java @@ -12,6 +12,8 @@ import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.team.TeamEvent; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; @@ -91,6 +93,9 @@ public class IslandTeamSetownerCommand extends CompositeCommand { IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false) .reason(IslandEvent.Reason.RANK_CHANGE).rankChange(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK) .build(); + // Add historu record + island.log(new LogEntry.Builder(LogType.NEWOWNER).data(targetUUID2.toString(), "new owner") + .data(user.getUniqueId().toString(), "old owner").build()); return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java index 38c0349f7..435bbdee5 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java @@ -9,6 +9,8 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.TeamInvite.Type; @@ -110,6 +112,10 @@ public class IslandTeamTrustCommand extends CompositeCommand { island.setRank(target, RanksManager.TRUSTED_RANK); user.sendMessage("commands.island.team.trust.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName()); target.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); + // Add historu record + island.log(new LogEntry.Builder(LogType.TRUSTED).data(targetUUID.toString(), "trusted") + .data(user.getUniqueId().toString(), "trusted by").build()); + } return true; } else { diff --git a/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java b/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java index 583bd24a9..012623479 100644 --- a/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java +++ b/src/main/java/world/bentobox/bentobox/api/logs/LogEntry.java @@ -15,16 +15,21 @@ import com.google.gson.annotations.Expose; * An {@link world.bentobox.bentobox.database.objects.adapters.AdapterInterface AdapterInterface} is provided to be able to save/retrieve * a list of instances of this object to/from the database: {@link world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter LogEntryListAdapter}. * - * @author Poslovitch + * @author Poslovitch, tastybento + * */ public class LogEntry { @Expose private final long timestamp; @Expose - private final String type; + private final LogType type; @Expose private final Map data; + public enum LogType { + REMOVE, ADD, UNREGISTER, BAN, UNOWNED, SPAWN, UNBAN, JOINED, NEWOWNER, TRUSTED, UNKNOWN + } + private LogEntry(@NonNull Builder builder) { this.timestamp = builder.timestamp; this.type = builder.type; @@ -36,7 +41,7 @@ public class LogEntry { } @NonNull - public String getType() { + public LogType getType() { return type; } @@ -47,12 +52,12 @@ public class LogEntry { public static class Builder { private long timestamp; - private final String type; + private final LogType type; private Map data; - public Builder(@NonNull String type) { + public Builder(LogType type) { this.timestamp = System.currentTimeMillis(); - this.type = type.toUpperCase(Locale.ENGLISH); + this.type = type; this.data = new LinkedHashMap<>(); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index e55c6a0ea..663c31cf0 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -37,6 +37,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.metadata.MetaDataAble; import world.bentobox.bentobox.api.metadata.MetaDataValue; import world.bentobox.bentobox.api.user.User; @@ -327,7 +328,7 @@ public class Island implements DataObject, MetaDataAble { public boolean ban(@NonNull UUID issuer, @NonNull UUID target) { if (getRank(target) != RanksManager.BANNED_RANK) { setRank(target, RanksManager.BANNED_RANK); - log(new LogEntry.Builder("BAN").data("player", target.toString()).data("issuer", issuer.toString()) + log(new LogEntry.Builder(LogType.BAN).data("player", target.toString()).data("issuer", issuer.toString()) .build()); setChanged(); } @@ -360,7 +361,7 @@ public class Island implements DataObject, MetaDataAble { */ public boolean unban(@NonNull UUID issuer, @NonNull UUID target) { if (members.remove(target) != null) { - log(new LogEntry.Builder("UNBAN").data("player", target.toString()).data("issuer", issuer.toString()) + log(new LogEntry.Builder(LogType.UNBAN).data("player", target.toString()).data("issuer", issuer.toString()) .build()); return true; } @@ -1132,7 +1133,7 @@ public class Island implements DataObject, MetaDataAble { this.owner = owner; if (owner == null) { - log(new LogEntry.Builder("UNOWNED").build()); + log(new LogEntry.Builder(LogType.UNOWNED).build()); return; } // Defensive code: demote any previous owner @@ -1281,7 +1282,7 @@ public class Island implements DataObject, MetaDataAble { setFlagsDefaults(); setFlag(Flags.LOCK, RanksManager.VISITOR_RANK); } - log(new LogEntry.Builder("SPAWN").data("value", String.valueOf(isSpawn)).build()); + log(new LogEntry.Builder(LogType.SPAWN).data("value", String.valueOf(isSpawn)).build()); setChanged(); } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapter.java b/src/main/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapter.java index a4f8b5954..b687263c0 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapter.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/adapters/LogEntryListAdapter.java @@ -5,7 +5,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import com.google.common.base.Enums; + import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; /** * @author Poslovitch @@ -41,7 +44,7 @@ public class LogEntryListAdapter implements AdapterInterface, Lis List> serialized = (List>) object; for (Map entry : serialized) { long timestamp = (long) entry.get(TIMESTAMP); - String type = (String) entry.get(TYPE); + LogType type = Enums.getIfPresent(LogType.class, (String) entry.get(TYPE)).or(LogType.UNKNOWN); Map data = (Map) entry.get(DATA); result.add(new LogEntry.Builder(type).timestamp(timestamp).data(data).build()); diff --git a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java index 03b67d59b..f79a69bf2 100644 --- a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java +++ b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java @@ -3,7 +3,10 @@ package world.bentobox.bentobox.lists; import java.text.DateFormat; import java.time.Instant; import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNull; @@ -11,6 +14,8 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.placeholders.GameModePlaceholderReplacer; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -120,6 +125,14 @@ public enum GameModePlaceholder { * @since 1.5.0 */ ISLAND_MEMBERS_COUNT("island_members_count", (addon, user, island) -> island == null ? "" : String.valueOf(island.getMemberSet().size())), + + /** + * Returns the number of players that are or have ever been a MEMBER on this island. + * @since 3.0.0 + */ + ISLAND_HISTORICAL_MEMBERS_COUNT("island_historical_members_count", + (addon, user, island) -> island == null ? "" : getHistoricalMembers(island)), + /** * Returns a comma separated list of player names that are at least MEMBER on this island. * @since 1.13.0 @@ -395,6 +408,24 @@ public enum GameModePlaceholder { this.replacer = replacer; } + /** + * Provides a count of how many players have ever joined the island as a member including the owner + * @param island island + * @return String count of the number of members + */ + private static String getHistoricalMembers(@Nullable Island island) { + Set uniqueMembers = new HashSet<>(); + for (LogEntry le : island.getHistory()) { + if (le.getType() == LogType.JOINED) { + Iterator it = le.getData().keySet().iterator(); + while (it.hasNext()) { + uniqueMembers.add(it.next()); + } + } + } + return String.valueOf(uniqueMembers.size()); + } + /** * Get the visited island * @param addon - game mode addon diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 9aceb1595..0452c970c 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -47,6 +47,8 @@ import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; @@ -1189,6 +1191,7 @@ public class IslandsManager { * * @param player player */ + @SuppressWarnings("deprecation") private void readyPlayer(@NonNull Player player) { // Stop any gliding player.setGliding(false); @@ -1537,6 +1540,8 @@ public class IslandsManager { // Add player to new island teamIsland.addMember(playerUUID); islandCache.addPlayer(playerUUID, teamIsland); + // Add historu record + teamIsland.log(new LogEntry.Builder(LogType.JOINED).data(playerUUID.toString(), "player").build()); // Save the island updateIsland(teamIsland); } diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java index a1c0c6859..d4b04400c 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -23,6 +23,8 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.Flag; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; @@ -431,6 +433,8 @@ public class IslandCache { } island.removeMember(uuid); island.removePrimary(uuid); + // Add historu record + island.log(new LogEntry.Builder(LogType.REMOVE).data(uuid.toString(), "player").build()); } /** diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index bb4f2cb22..198e8950c 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -15,6 +15,8 @@ import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandCreateEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; +import world.bentobox.bentobox.api.logs.LogEntry; +import world.bentobox.bentobox.api.logs.LogEntry.LogType; import world.bentobox.bentobox.api.events.island.IslandResetEvent; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -217,6 +219,8 @@ public class NewIsland { island.setFlagsDefaults(); // Register metrics plugin.getMetrics().ifPresent(BStats::increaseIslandsCreatedCount); + // Add historu record + island.log(new LogEntry.Builder(LogType.JOINED).data(user.getUniqueId().toString(), "owner").build()); // Save island IslandsManager.updateIsland(island); }