summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md13
-rw-r--r--gradle.properties2
-rw-r--r--src/main/java/com/keuin/blame/Blame.java47
-rw-r--r--src/main/java/com/keuin/blame/command/BlameBlockCommand.java8
-rw-r--r--src/main/java/com/keuin/blame/command/BlameStatCommand.java111
-rw-r--r--src/main/java/com/keuin/blame/data/entry/LogEntry.java5
-rw-r--r--src/main/java/com/keuin/blame/data/entry/LogEntryNames.java (renamed from src/main/java/com/keuin/blame/data/entry/LogEntryNamesV1.java)3
-rw-r--r--src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java22
-rw-r--r--src/main/java/com/keuin/blame/data/transformer/AbstractLogEntryTransformer.java7
-rw-r--r--src/main/java/com/keuin/blame/data/transformer/LogEntryV1ToV2Transformer.java34
-rw-r--r--src/main/java/com/keuin/blame/data/transformer/TransformerManager.java32
-rw-r--r--src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java6
-rw-r--r--src/main/java/com/keuin/blame/util/DatabaseUtil.java15
13 files changed, 283 insertions, 22 deletions
diff --git a/README.md b/README.md
index fd96346..fc4e89a 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,12 @@
-# Fabric Example Mod
+# Blame
-## Setup
+When you find something has been messed up in the Minecraft server you're managing, then who to blame?
-For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using.
+*Blame* records players' interactions with blocks and entities, thus enabling you to find out the unruly player.
-## License
+## Usage
-This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects.
+- Install MongoDB
+- Configure your database in `blame.json` sitting with the server `.jar` file
+- Start your server
+- Use command `/blame` to look up the history \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 2527577..aa2141a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@ minecraft_version=1.15.2
yarn_mappings=1.15.2+build.17
loader_version=0.10.8
# Mod Properties
-mod_version=1.2.0-alpha
+mod_version=1.3.1-alpha
maven_group=com.keuin.blame
archives_base_name=blame-fabric
# Dependencies
diff --git a/src/main/java/com/keuin/blame/Blame.java b/src/main/java/com/keuin/blame/Blame.java
index ab22590..a4c806d 100644
--- a/src/main/java/com/keuin/blame/Blame.java
+++ b/src/main/java/com/keuin/blame/Blame.java
@@ -5,8 +5,10 @@ import com.keuin.blame.adapter.*;
import com.keuin.blame.adapter.handler.PlaceBlockHandler;
import com.keuin.blame.command.BlameBlockCommand;
import com.keuin.blame.command.BlameLimitCommand;
+import com.keuin.blame.command.BlameStatCommand;
import com.keuin.blame.config.BlameConfig;
import com.keuin.blame.lookup.LookupManager;
+import com.keuin.blame.util.DatabaseUtil;
import com.keuin.blame.util.PrintUtil;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
@@ -24,12 +26,10 @@ import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.util.logging.Level;
-import java.util.logging.Logger;
public class Blame implements ModInitializer {
- private static final Logger logger = Logger.getLogger(Blame.class.getName());
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Blame.class.getName());
public static BlameConfig config;
@@ -53,9 +53,32 @@ public class Blame implements ModInitializer {
return true;
}
- private static void disableMongoSpamming() {
- Logger mongoLogger = Logger.getLogger("org.mongodb.driver");
- mongoLogger.setLevel(Level.WARNING);
+
+
+ private static void upgradeOldLogEntries() {
+// try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) {
+// final MongoDatabase db = mongoClient.getDatabase(
+// DatabaseUtil.MONGO_CONFIG.getDatabaseName()
+// );
+// final MongoCollection<LogEntry> collection = db.getCollection(
+// DatabaseUtil.MONGO_CONFIG.getLogCollectionName(), LogEntry.class
+// );
+// collection.updateMany()
+// FindIterable<LogEntry> iterable =
+// collection.find(Filters.ne(LogEntryNames.VERSION, TransformerManager.LATEST_VERSION))
+// .showRecordId(true);
+// for (LogEntry logEntry : iterable) {
+// if (logEntry.version > TransformerManager.LATEST_VERSION) {
+// logger.warning("Detected a newer entry in the database! " +
+// "Downgrading of Blame is not supported and may cause " +
+// "unexpected behaviour.");
+// continue;
+// }
+//
+// collection.updateOne(iterable.showRecordId())
+// }
+//
+// }
}
@Override
@@ -67,7 +90,7 @@ public class Blame implements ModInitializer {
if (!loadConfig())
return;
- disableMongoSpamming();
+ DatabaseUtil.disableMongoSpamming();
// hook disable event
ServerLifecycleEvents.SERVER_STOPPING.register(new ServerLifecycleEvents.ServerStopping() {
@@ -102,11 +125,17 @@ public class Blame implements ModInitializer {
.then(CommandManager.argument("y", IntegerArgumentType.integer())
.then(CommandManager.argument("z", IntegerArgumentType.integer())
.then(CommandManager.argument("world", StringArgumentType.greedyString())
- .executes(BlameBlockCommand::blameBlock)))))));
+ .executes(BlameBlockCommand::blameGivenBlockPos))))))
+ );
commandDispatcher.register(
CommandManager.literal("blame").then(CommandManager.literal("limit")
.then(CommandManager.argument("limit", IntegerArgumentType.integer(1, 255))
- .executes(BlameLimitCommand::setLimit))));
+ .executes(BlameLimitCommand::setLimit)))
+ );
+ commandDispatcher.register(
+ CommandManager.literal("blame").then(CommandManager.literal("stat")
+ .executes(BlameStatCommand::showStat))
+ );
}
});
}
diff --git a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
index 578870d..4fdd350 100644
--- a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
+++ b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
@@ -16,8 +16,7 @@ import static com.keuin.blame.command.Commands.SUCCESS;
public class BlameBlockCommand {
-
- public static int blameBlock(CommandContext<ServerCommandSource> context) {
+ public static int blameGivenBlockPos(CommandContext<ServerCommandSource> context) {
Entity entity = context.getSource().getEntity();
if (!(entity instanceof ServerPlayerEntity)) {
// can only be executed by player
@@ -39,6 +38,10 @@ public class BlameBlockCommand {
return SUCCESS;
}
+ public static int blameGivenBlockRange(CommandContext<ServerCommandSource> context) {
+ return SUCCESS;
+ }
+
private static class Callback implements LookupCallback {
private final CommandContext<ServerCommandSource> context;
@@ -63,4 +66,5 @@ public class BlameBlockCommand {
PrintUtil.msgInfo(context, printBuilder.toString());
}
}
+
}
diff --git a/src/main/java/com/keuin/blame/command/BlameStatCommand.java b/src/main/java/com/keuin/blame/command/BlameStatCommand.java
new file mode 100644
index 0000000..fade0a1
--- /dev/null
+++ b/src/main/java/com/keuin/blame/command/BlameStatCommand.java
@@ -0,0 +1,111 @@
+package com.keuin.blame.command;
+
+import com.google.common.base.Strings;
+import com.keuin.blame.data.entry.LogEntry;
+import com.keuin.blame.data.helper.VersionedLogEntryHelper;
+import com.keuin.blame.util.DatabaseUtil;
+import com.keuin.blame.util.PrintUtil;
+import com.mojang.brigadier.context.CommandContext;
+import com.mongodb.MongoClientException;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import net.minecraft.server.command.ServerCommandSource;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.logging.Logger;
+
+public class BlameStatCommand {
+
+ private static final Logger logger = Logger.getLogger(BlameStatCommand.class.getName());
+
+ public static int showStat(CommandContext<ServerCommandSource> context) {
+ PrintUtil.msgInfo(context, "Collecting statistics. This may take a few seconds...");
+ showStat(new ShowStatCallback() {
+ @Override
+ public void showStat(@Nullable BlameStat stat) {
+ StringBuilder sb = new StringBuilder();
+ if (stat != null) {
+ sb.append("Statistics\n");
+ sb.append("====\n");
+ sb.append("# Count by subjects\n");
+ stat.getCountMap().forEach((subjectId, count) -> {
+ sb.append("<").append(
+ Optional.ofNullable(Strings.emptyToNull(subjectId)).orElse("null")
+ ).append(">: ").append(count).append("\n");
+ });
+ sb.append("=== END ===");
+ } else {
+ sb.append("Failed to get statistics. Please refer to server log for more information.");
+ }
+ PrintUtil.msgInfo(context, sb.toString());
+ }
+ });
+ return Commands.SUCCESS;
+ }
+
+ public static void showStat(ShowStatCallback callback) {
+ Objects.requireNonNull(callback);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("Collecting statistics...");
+ try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) {
+ final MongoDatabase db = mongoClient.getDatabase(
+ DatabaseUtil.MONGO_CONFIG.getDatabaseName()
+ );
+ final MongoCollection<LogEntry> collection = db.getCollection(
+ DatabaseUtil.MONGO_CONFIG.getLogCollectionName(), LogEntry.class
+ );
+ Collection<String> ids = VersionedLogEntryHelper.getLoggedSubjectsId(collection);
+
+ // count by distinct subjects
+ Map<String, Long> countMap = new HashMap<>();
+ for (String subjectId : ids) {
+ long count = VersionedLogEntryHelper.countBySubjectId(collection, subjectId);
+ countMap.put(subjectId, count);
+ }
+
+ // invoke callback
+ callback.showStat(new BlameStat(countMap));
+ } catch (MongoClientException exception) {
+ logger.severe("Failed when querying the database: " + exception +
+ ". Failed to get statistics.");
+ callback.showStat(null);
+ }
+ }
+ }, "BlameStatCommandThread").start();
+ }
+
+ public static class BlameStat {
+ private final Map<String, Long> countMap;
+
+ public BlameStat(Map<String, Long> countMap) {
+ this.countMap = countMap;
+ }
+
+ public Map<String, Long> getCountMap() {
+ return Collections.unmodifiableMap(countMap);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BlameStat blameStat = (BlameStat) o;
+ return Objects.equals(countMap, blameStat.countMap);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(countMap);
+ }
+ }
+
+ public interface ShowStatCallback {
+ void showStat(@Nullable BlameStat stat);
+ }
+
+}
diff --git a/src/main/java/com/keuin/blame/data/entry/LogEntry.java b/src/main/java/com/keuin/blame/data/entry/LogEntry.java
index e47cc92..04f52ca 100644
--- a/src/main/java/com/keuin/blame/data/entry/LogEntry.java
+++ b/src/main/java/com/keuin/blame/data/entry/LogEntry.java
@@ -11,7 +11,7 @@ import org.bson.codecs.pojo.annotations.BsonProperty;
import java.util.Objects;
import java.util.UUID;
-import static com.keuin.blame.data.entry.LogEntryNamesV1.*;
+import static com.keuin.blame.data.entry.LogEntryNames.*;
public class LogEntry {
@@ -70,6 +70,9 @@ public class LogEntry {
@BsonProperty(OBJECT_POS)
public WorldPos objectPos = WorldPos.NULL_POS;
+ @BsonProperty(RADIUS)
+ public double radius = 0;
+
public LogEntry() {
}
diff --git a/src/main/java/com/keuin/blame/data/entry/LogEntryNamesV1.java b/src/main/java/com/keuin/blame/data/entry/LogEntryNames.java
index d21be97..e375d8e 100644
--- a/src/main/java/com/keuin/blame/data/entry/LogEntryNamesV1.java
+++ b/src/main/java/com/keuin/blame/data/entry/LogEntryNames.java
@@ -1,6 +1,6 @@
package com.keuin.blame.data.entry;
-public class LogEntryNamesV1 {
+public class LogEntryNames {
public static final String VERSION = "version";
public static final String GAME_VERSION = "game_version";
public static final String TIMESTAMP_MILLIS = "timestamp_millis";
@@ -11,4 +11,5 @@ public class LogEntryNamesV1 {
public static final String OBJECT_TYPE = "object_type";
public static final String OBJECT_ID = "object_id";
public static final String OBJECT_POS = "object_pos";
+ public static final String RADIUS = "radius";
}
diff --git a/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java b/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java
new file mode 100644
index 0000000..064ae87
--- /dev/null
+++ b/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java
@@ -0,0 +1,22 @@
+package com.keuin.blame.data.helper;
+
+import com.keuin.blame.data.entry.LogEntry;
+import com.keuin.blame.data.entry.LogEntryNames;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class VersionedLogEntryHelper {
+ public static Collection<String> getLoggedSubjectsId(MongoCollection<LogEntry> collection) {
+ List<String> list = new ArrayList<>();
+ for (String s : collection.distinct(LogEntryNames.SUBJECT_ID, String.class))
+ list.add(s);
+ return list;
+ }
+ public static long countBySubjectId(MongoCollection<LogEntry> collection, String subjectId) {
+ return collection.countDocuments(Filters.eq(LogEntryNames.SUBJECT_ID, subjectId));
+ }
+}
diff --git a/src/main/java/com/keuin/blame/data/transformer/AbstractLogEntryTransformer.java b/src/main/java/com/keuin/blame/data/transformer/AbstractLogEntryTransformer.java
new file mode 100644
index 0000000..78872c5
--- /dev/null
+++ b/src/main/java/com/keuin/blame/data/transformer/AbstractLogEntryTransformer.java
@@ -0,0 +1,7 @@
+package com.keuin.blame.data.transformer;
+
+import com.keuin.blame.data.entry.LogEntry;
+
+public abstract class AbstractLogEntryTransformer {
+ public abstract LogEntry transform(LogEntry entry);
+}
diff --git a/src/main/java/com/keuin/blame/data/transformer/LogEntryV1ToV2Transformer.java b/src/main/java/com/keuin/blame/data/transformer/LogEntryV1ToV2Transformer.java
new file mode 100644
index 0000000..4f5c660
--- /dev/null
+++ b/src/main/java/com/keuin/blame/data/transformer/LogEntryV1ToV2Transformer.java
@@ -0,0 +1,34 @@
+package com.keuin.blame.data.transformer;
+
+import com.keuin.blame.data.WorldPos;
+import com.keuin.blame.data.entry.LogEntry;
+
+public class LogEntryV1ToV2Transformer extends AbstractLogEntryTransformer {
+
+ private static final LogEntryV1ToV2Transformer INSTANCE = new LogEntryV1ToV2Transformer();
+
+ static {
+ TransformerManager.setTransformer(1, INSTANCE);
+ }
+
+ @Override
+ public LogEntry transform(LogEntry entry) {
+ LogEntry entryV2 = new LogEntry();
+ entryV2.version = 2;
+ entryV2.gameVersion = entry.gameVersion;
+ entryV2.timeMillis = entry.timeMillis;
+ entryV2.subjectId = entry.subjectId;
+ entryV2.subjectUUID = entry.subjectUUID;
+ entryV2.subjectPos = entry.subjectPos;
+ entryV2.actionType = entry.actionType;
+ entryV2.objectType = entry.objectType;
+ entryV2.objectId = entry.objectId;
+ entryV2.objectPos = entry.objectPos;
+ entryV2.radius = getRadius(entry.objectPos);
+ return entryV2;
+ }
+
+ private static double getRadius(WorldPos objectPos) {
+ return Math.sqrt(Math.pow(objectPos.getX(), 2) + Math.pow(objectPos.getY(), 2) + Math.pow(objectPos.getZ(), 2));
+ }
+}
diff --git a/src/main/java/com/keuin/blame/data/transformer/TransformerManager.java b/src/main/java/com/keuin/blame/data/transformer/TransformerManager.java
new file mode 100644
index 0000000..645c990
--- /dev/null
+++ b/src/main/java/com/keuin/blame/data/transformer/TransformerManager.java
@@ -0,0 +1,32 @@
+package com.keuin.blame.data.transformer;
+
+import com.keuin.blame.data.entry.LogEntry;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class TransformerManager {
+
+ public static int LATEST_VERSION = 2;
+
+ private static final Map<Integer, AbstractLogEntryTransformer> transformerMap = new HashMap<>();
+
+ static void setTransformer(int baseVersion, AbstractLogEntryTransformer transformer) {
+ transformerMap.put(baseVersion, transformer);
+ }
+
+ public static Object toLatestVersion(LogEntry baseEntry) {
+ return transformTo(LATEST_VERSION, baseEntry);
+ }
+
+ public static Object transformTo(int targetVersion, LogEntry baseEntry) {
+ Objects.requireNonNull(baseEntry);
+ if (baseEntry.version > targetVersion)
+ throw new RuntimeException("Downgrade is not supported.");
+ LogEntry entry = baseEntry;
+ for (int currentVersion = baseEntry.version; currentVersion != targetVersion; ++currentVersion)
+ entry = transformerMap.get(currentVersion + 1).transform(entry);
+ return entry;
+ }
+}
diff --git a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java b/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java
index a4b2475..d7cec43 100644
--- a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java
+++ b/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java
@@ -2,7 +2,7 @@ package com.keuin.blame.lookup;
import com.keuin.blame.data.WorldPos;
import com.keuin.blame.data.entry.LogEntry;
-import com.keuin.blame.data.entry.LogEntryNamesV1;
+import com.keuin.blame.data.entry.LogEntryNames;
import com.mongodb.client.FindIterable;
import com.mongodb.client.model.Filters;
@@ -16,8 +16,8 @@ public class BlockPosLookupFilter extends AbstractLookupFilter {
@Override
FindIterable<LogEntry> find(FindIterable<LogEntry> iterable) {
return iterable.filter(Filters.and(
- Filters.eq(LogEntryNamesV1.VERSION, 1),
- Filters.eq(LogEntryNamesV1.OBJECT_POS, blockPos)
+ Filters.eq(LogEntryNames.VERSION, 1),
+ Filters.eq(LogEntryNames.OBJECT_POS, blockPos)
));
}
}
diff --git a/src/main/java/com/keuin/blame/util/DatabaseUtil.java b/src/main/java/com/keuin/blame/util/DatabaseUtil.java
index 45ea98a..d68bc4d 100644
--- a/src/main/java/com/keuin/blame/util/DatabaseUtil.java
+++ b/src/main/java/com/keuin/blame/util/DatabaseUtil.java
@@ -1,5 +1,8 @@
package com.keuin.blame.util;
+//import ch.qos.logback.classic.Level;
+//import ch.qos.logback.classic.LoggerContext;
+
import com.keuin.blame.Blame;
import com.keuin.blame.config.MongoConfig;
import com.keuin.blame.data.enums.codec.ActionTypeCodec;
@@ -11,6 +14,9 @@ import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
public class DatabaseUtil {
@@ -34,4 +40,13 @@ public class DatabaseUtil {
// db.log.createIndex({ timestamp_millis: -1 })
// db.log.createIndex({ timestamp_millis: -1, object_id: "hashed" })
// db.log.createIndex({ timestamp_millis: -1, subject_id: "hashed" })
+
+ public static void disableMongoSpamming() {
+// ((LoggerContext) LoggerFactory.getILoggerFactory()).getLogger("org.mongodb.driver").setLevel(Level.ERROR);
+ String[] mongoDrivers = new String[] {"org.mongodb", "org.mongodb.driver", "org.mongodb.driver.cluster", "org.mongodb.driver.connection"};
+ for (String driverName : mongoDrivers) {
+ Logger mongoLogger = Logger.getLogger(driverName);
+ mongoLogger.setLevel(Level.OFF); // Plz be silent, my boy.
+ }
+ }
}