Reworked logging framework
This commit is contained in:
@@ -13,11 +13,11 @@ import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.login.PlayerParser;
|
||||
import core.game.node.entity.state.EntityState;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.system.monitor.PlayerMonitor;
|
||||
import core.plugin.Plugin;
|
||||
import core.tools.RandomFunction;
|
||||
import kotlin.Unit;
|
||||
import rs09.game.content.global.action.EquipHandler;
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor;
|
||||
import rs09.game.system.config.ItemConfigParser;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
@@ -311,7 +311,7 @@ public final class DuelSession extends ComponentPlugin {
|
||||
log = log.substring(0, log.length() - 1);
|
||||
}
|
||||
log += "}";
|
||||
player.getMonitor().log(log, PlayerMonitor.DUEL_LOG);
|
||||
PlayerMonitor.logMisc(player, "Duel", log);
|
||||
} else {
|
||||
player.removeExtension(DuelSession.class);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import core.game.node.entity.skill.summoning.familiar.FamiliarManager;
|
||||
import core.game.node.item.GroundItemManager;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.system.communication.CommunicationInfo;
|
||||
import core.game.system.monitor.PlayerMonitor;
|
||||
import core.game.system.task.Pulse;
|
||||
import core.game.world.map.*;
|
||||
import core.game.world.map.build.DynamicRegion;
|
||||
@@ -73,6 +72,7 @@ import rs09.game.node.entity.combat.equipment.EquipmentDegrader;
|
||||
import rs09.game.node.entity.player.graves.Grave;
|
||||
import rs09.game.node.entity.player.graves.GraveType;
|
||||
import rs09.game.node.entity.player.graves.GraveController;
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor;
|
||||
import rs09.game.node.entity.player.info.login.PlayerSaver;
|
||||
import rs09.game.node.entity.skill.runecrafting.PouchManager;
|
||||
import rs09.game.node.entity.state.newsys.State;
|
||||
@@ -88,6 +88,7 @@ import rs09.tools.TickUtilsKt;
|
||||
import rs09.worker.ManagementEvents;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static api.ContentAPIKt.*;
|
||||
import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE;
|
||||
@@ -255,11 +256,6 @@ public class Player extends Entity {
|
||||
* The farming manager.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The monitor which monitors player actions.
|
||||
*/
|
||||
private final PlayerMonitor monitor = new PlayerMonitor();
|
||||
|
||||
/**
|
||||
* Represents the players warning messages.
|
||||
*/
|
||||
@@ -383,6 +379,7 @@ public class Player extends Entity {
|
||||
details.save();
|
||||
sendLogoutEvents();
|
||||
Repository.getDisconnectionQueue().add(this);
|
||||
checkForWealthUpdate(true);
|
||||
}
|
||||
|
||||
private void sendLogoutEvents() {
|
||||
@@ -475,6 +472,39 @@ public class Player extends Entity {
|
||||
|
||||
//Decrements prayer points
|
||||
getPrayer().tick();
|
||||
|
||||
//update wealth tracking
|
||||
checkForWealthUpdate(false);
|
||||
}
|
||||
|
||||
private void checkForWealthUpdate(boolean force) {
|
||||
if (isArtificial()) return;
|
||||
long previousWealth = getAttribute("last-wealth", -1L);
|
||||
long lastWealthCheck = getAttribute("last-wealth-check", -1L);
|
||||
|
||||
long nowTime = System.currentTimeMillis();
|
||||
if (force || nowTime - lastWealthCheck >= TimeUnit.MINUTES.toMillis(5)) {
|
||||
long totalWealth = 0L;
|
||||
for (Item i : inventory.toArray()) {
|
||||
if (i == null) continue;
|
||||
totalWealth += (long) i.getDefinition().getValue() * i.getAmount();
|
||||
}
|
||||
for (Item i : bank.toArray()) {
|
||||
if (i == null) break;
|
||||
totalWealth += (long) i.getDefinition().getValue() * i.getAmount();
|
||||
}
|
||||
for (Item i : bankSecondary.toArray()) {
|
||||
if (i == null) break;
|
||||
totalWealth += (long) i.getDefinition().getValue() * i.getAmount();
|
||||
}
|
||||
|
||||
long diff = previousWealth == -1 ? 0L : totalWealth - previousWealth;
|
||||
setAttribute("/save:last-wealth", totalWealth);
|
||||
setAttribute("/save:last-wealth-check", nowTime);
|
||||
|
||||
if (diff != 0)
|
||||
PlayerMonitor.logWealthChange(this, totalWealth, diff);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -611,8 +641,11 @@ public class Player extends Entity {
|
||||
Grave g = GraveController.produceGrave(GraveController.getGraveType(this));
|
||||
g.initialize(this, location, Arrays.stream(c[1].toArray()).filter(Objects::nonNull).toArray(Item[]::new)); //note: the amount of code required to filter nulls from an array in Java is atrocious.
|
||||
} else {
|
||||
StringBuilder itemsLost = new StringBuilder();
|
||||
for (Item item : c[1].toArray()) {
|
||||
if (item == null) continue;
|
||||
if (killer instanceof Player)
|
||||
itemsLost.append(getItemName(item.getId())).append("(").append(item.getAmount()).append("), ");
|
||||
if (GraveController.shouldCrumble(item.getId()))
|
||||
continue;
|
||||
if (GraveController.shouldRelease(item.getId()))
|
||||
@@ -620,6 +653,8 @@ public class Player extends Entity {
|
||||
item = GraveController.checkTransform(item);
|
||||
GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this);
|
||||
}
|
||||
if (killer instanceof Player)
|
||||
PlayerMonitor.logMisc((Player) killer, "PK", "Killed " + name + ", who dropped: " + itemsLost);
|
||||
sendMessage(colorize("%RDue to the circumstances of your death, you do not have a grave."));
|
||||
}
|
||||
|
||||
@@ -1173,13 +1208,6 @@ public class Player extends Entity {
|
||||
* @return The farmingManager.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the monitor.
|
||||
* @return The monitor.
|
||||
*/
|
||||
public PlayerMonitor getMonitor() {
|
||||
return monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warningMessages.
|
||||
|
||||
+7
-21
@@ -7,10 +7,9 @@ import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.request.RequestModule
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.monitor.PlayerMonitor
|
||||
import rs09.game.ai.AIRepository
|
||||
import rs09.game.ai.general.scriptrepository.DoublingMoney
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -352,17 +351,16 @@ class TradeModule
|
||||
* @param module the module.
|
||||
*/
|
||||
private fun giveContainers(module: TradeModule) {
|
||||
val pContainer: Container? = module.container
|
||||
val oContainer: Container? = getExtension(module.target)!!.container
|
||||
val pContainer: Container = module.container ?: return
|
||||
val oContainer: Container = getExtension(module.target)!!.container ?: return
|
||||
|
||||
SystemLogger.logTrade(module.player!!.username + " -> " + module.target!!.username + " " + Arrays.toString(pContainer!!.toArray()))
|
||||
SystemLogger.logTrade(module.target!!.username + " -> " + module.player!!.username + " " + Arrays.toString(oContainer!!.toArray()))
|
||||
PlayerMonitor.logTrade(module.player!!, module.target!!, pContainer, oContainer)
|
||||
|
||||
(AIRepository.PulseRepository[module.player!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.target!!, oContainer)
|
||||
(AIRepository.PulseRepository[module.target!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.player!!, pContainer)
|
||||
|
||||
addContainer(module.player, module.target, oContainer)
|
||||
addContainer(module.target, module.player, pContainer)
|
||||
addContainer(module.player, oContainer)
|
||||
addContainer(module.target, pContainer)
|
||||
module.target!!.packetDispatch.sendMessage("Accepted trade.")
|
||||
module.player!!.packetDispatch.sendMessage("Accepted trade.")
|
||||
}
|
||||
@@ -373,10 +371,9 @@ class TradeModule
|
||||
* @param target the target who gains the items.
|
||||
* @param container the container.
|
||||
*/
|
||||
private fun addContainer(player: Player?, target: Player?, container: Container?) {
|
||||
private fun addContainer(player: Player?, container: Container?) {
|
||||
val c = Container(container!!.itemCount(), ContainerType.ALWAYS_STACK)
|
||||
c.addAll(container)
|
||||
var log = "traded => " + target!!.name + " received => {"
|
||||
for (i in container.toArray()) {
|
||||
if (i == null) {
|
||||
continue
|
||||
@@ -388,17 +385,6 @@ class TradeModule
|
||||
GroundItemManager.create(i, player)
|
||||
}
|
||||
}
|
||||
for (i in c.toArray()) {
|
||||
if (i == null) {
|
||||
continue
|
||||
}
|
||||
log += i.name + " x " + i.amount + ","
|
||||
}
|
||||
if (log[log.length - 1] == ',') {
|
||||
log = log.substring(0, log.length - 1)
|
||||
}
|
||||
log += "}"
|
||||
player!!.monitor.log(log, PlayerMonitor.TRADE_LOG)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,10 @@ import core.game.world.update.flag.player.AppearanceFlag;
|
||||
import core.net.packet.PacketRepository;
|
||||
import core.net.packet.context.SkillContext;
|
||||
import core.net.packet.out.SkillLevel;
|
||||
import kotlin.Pair;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor;
|
||||
import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks;
|
||||
import rs09.game.world.GameWorld;
|
||||
import rs09.game.world.repository.Repository;
|
||||
@@ -23,6 +25,8 @@ import rs09.plugin.CorePluginTypes.XPGainPlugins;
|
||||
import org.rs09.consts.Items;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static java.lang.Math.floor;
|
||||
import static java.lang.Math.max;
|
||||
@@ -68,6 +72,10 @@ public final class Skills {
|
||||
*/
|
||||
private final double[] experience;
|
||||
|
||||
private double[] lastUpdateXp = null;
|
||||
|
||||
private int lastUpdate = GameWorld.getTicks();
|
||||
|
||||
/**
|
||||
* An array containing all the maximum levels.
|
||||
*/
|
||||
@@ -212,6 +220,8 @@ public final class Skills {
|
||||
* @param experience The experience.
|
||||
*/
|
||||
public void addExperience(int slot, double experience, boolean playerMod) {
|
||||
if (lastUpdateXp == null)
|
||||
lastUpdateXp = this.experience.clone();
|
||||
double mod = getExperienceMod(slot, experience, playerMod, true);
|
||||
final Player player = entity instanceof Player ? ((Player) entity) : null;
|
||||
final AssistSession assist = entity.getExtension(AssistSession.class);
|
||||
@@ -278,6 +288,18 @@ public final class Skills {
|
||||
PacketRepository.send(SkillLevel.class, new SkillContext((Player) entity, slot));
|
||||
entity.dispatch(new XPGainEvent(slot, experienceAdd));
|
||||
}
|
||||
if (GameWorld.getTicks() - lastUpdate >= 200) {
|
||||
ArrayList<Pair<Integer,Double>> diffs = new ArrayList<>();
|
||||
for (int i = 0; i < this.experience.length; i++) {
|
||||
double diff = this.experience[i] - lastUpdateXp[i];
|
||||
if (diff != 0.0) {
|
||||
diffs.add(new Pair<>(i, diff));
|
||||
}
|
||||
}
|
||||
PlayerMonitor.logXpGains(player, diffs);
|
||||
lastUpdateXp = this.experience.clone();
|
||||
lastUpdate = GameWorld.getTicks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import rs09.Server;
|
||||
import rs09.ServerConstants;
|
||||
import rs09.ServerStore;
|
||||
import rs09.game.ai.AIRepository;
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
import rs09.game.world.repository.Repository;
|
||||
@@ -55,6 +56,7 @@ public final class SystemTermination {
|
||||
}
|
||||
}
|
||||
GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown);
|
||||
PlayerMonitor.flushRemainingEventsImmediately();
|
||||
ServerStore s = null;
|
||||
for (PersistWorld wld : GameWorld.getWorldPersists()) {
|
||||
if (wld instanceof ServerStore)
|
||||
|
||||
@@ -5,9 +5,9 @@ import core.game.content.activity.ActivityPlugin;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.PlayerDetails;
|
||||
import core.game.node.entity.player.info.Rights;
|
||||
import core.game.system.monitor.PlayerMonitor;
|
||||
import proto.management.ClanJoinNotification;
|
||||
import proto.management.ClanLeaveNotification;
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor;
|
||||
import rs09.game.world.GameWorld;
|
||||
import rs09.game.world.repository.Repository;
|
||||
import core.net.amsc.WorldCommunicator;
|
||||
@@ -198,9 +198,7 @@ public final class ClanRepository {
|
||||
return;
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(message);
|
||||
sb.append(" => ").append(name).append(" (owned by ").append(owner).append(")");
|
||||
player.getMonitor().log(sb.toString(), PlayerMonitor.CLAN_CHAT_LOG);
|
||||
PlayerMonitor.logChat(player, "clan", message);
|
||||
for (Iterator<ClanEntry> it = players.iterator(); it.hasNext();) {
|
||||
ClanEntry entry = it.next();
|
||||
Player p = entry.getPlayer();
|
||||
|
||||
@@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import proto.management.PrivateMessage;
|
||||
import rs09.auth.UserAccountInfo;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import core.game.system.monitor.PlayerMonitor;
|
||||
import core.game.system.mysql.SQLTable;
|
||||
import core.game.system.task.Pulse;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
package core.game.system.monitor;
|
||||
|
||||
import core.cache.misc.buffer.ByteBufferUtils;
|
||||
import core.game.node.entity.player.Player;
|
||||
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Handles the player monitoring.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class PlayerMonitor {
|
||||
|
||||
/**
|
||||
* The public chat log.
|
||||
*/
|
||||
public static final int PUBLIC_CHAT_LOG = 0;
|
||||
|
||||
/**
|
||||
* The private chat log.
|
||||
*/
|
||||
public static final int PRIVATE_CHAT_LOG = 1;
|
||||
|
||||
/**
|
||||
* The clan chat log.
|
||||
*/
|
||||
public static final int CLAN_CHAT_LOG = 2;
|
||||
|
||||
/**
|
||||
* The IP/MAC-address log.
|
||||
*/
|
||||
public static final int ADDRESS_LOG = 3;
|
||||
|
||||
/**
|
||||
* The command log.
|
||||
*/
|
||||
public static final int COMMAND_LOG = 4;
|
||||
|
||||
/**
|
||||
* The trade log.
|
||||
*/
|
||||
public static final int TRADE_LOG = 5;
|
||||
|
||||
/**
|
||||
* The GE logs.
|
||||
*/
|
||||
public static final int GRAND_EXCHANGE_LOG = 6;
|
||||
|
||||
/**
|
||||
* The duel stake logs.
|
||||
*/
|
||||
public static final int DUEL_LOG = 7;
|
||||
|
||||
/**
|
||||
* The macro flag for a click when the client was out of focus.
|
||||
*/
|
||||
public static final int MF_NO_FOCUS_CLICK = 0x1;
|
||||
|
||||
/**
|
||||
* The networth of the player.
|
||||
*/
|
||||
private long networth;
|
||||
|
||||
/**
|
||||
* If the client is currently focused.
|
||||
*/
|
||||
private boolean clientFocus = true;
|
||||
|
||||
/**
|
||||
* How likely it is this player has used a macro.
|
||||
*/
|
||||
private int macroFlag;
|
||||
|
||||
/**
|
||||
* The duplication log.
|
||||
*/
|
||||
private DuplicationLog duplicationLog;
|
||||
|
||||
/**
|
||||
* The message monitors.
|
||||
*/
|
||||
private MessageLog[] logs = new MessageLog[8];
|
||||
|
||||
/**
|
||||
* Constructs a new {@code PlayerMonitor} {@code Object}.
|
||||
*/
|
||||
public PlayerMonitor() {
|
||||
logs[PUBLIC_CHAT_LOG] = new MessageLog(500);
|
||||
logs[PRIVATE_CHAT_LOG] = new MessageLog(500);
|
||||
logs[CLAN_CHAT_LOG] = new MessageLog(500);
|
||||
logs[ADDRESS_LOG] = new MessageLog(200);
|
||||
logs[COMMAND_LOG] = new MessageLog(200);
|
||||
logs[TRADE_LOG] = new MessageLog(200);
|
||||
logs[GRAND_EXCHANGE_LOG] = new MessageLog(500);
|
||||
logs[DUEL_LOG] = new MessageLog(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the logs.
|
||||
*/
|
||||
public void clear() {
|
||||
for (int i = 0; i < logs.length; i++) {
|
||||
if (logs[i] == null) {
|
||||
continue;
|
||||
}
|
||||
logs[i].getMessages().clear();
|
||||
}
|
||||
duplicationLog.getMessages().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player's actions should be logged.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isLogActions() {
|
||||
return duplicationLog != null && duplicationLog.isLoggingFlagged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given string.
|
||||
* @param string The string to log.
|
||||
* @param type The log type.
|
||||
*/
|
||||
public void log(String string, int type) {
|
||||
log(string, type, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given string.
|
||||
* @param string The string to log.
|
||||
* @param type The log type.
|
||||
* @param timeStamp If we should time stamp the logged string.
|
||||
*/
|
||||
public void log(String string, int type, boolean timeStamp) {
|
||||
if (type < 3) {
|
||||
String check = string.toLowerCase();
|
||||
if (check.contains("dupe") || check.contains("duping") || check.contains("bug") || check.contains("glitch") || check.contains("exploit")) {
|
||||
getDuplicationLog().flag(DuplicationLog.DUPE_TALK);
|
||||
String prefix = null;
|
||||
if (type == PUBLIC_CHAT_LOG) {
|
||||
prefix = "Public";
|
||||
} else if (type == PRIVATE_CHAT_LOG) {
|
||||
prefix = "Private";
|
||||
} else {
|
||||
prefix = "Clan";
|
||||
}
|
||||
getDuplicationLog().log(prefix + " chat message: " + string, true);
|
||||
}
|
||||
}
|
||||
logs[type].log(string, timeStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the networth difference for a player (called on logout).
|
||||
* @param player The player.
|
||||
* @param currentNet The current networth.
|
||||
*/
|
||||
public void checkNetworth(Player player, long currentNet) {
|
||||
long difference = currentNet - this.networth;
|
||||
if (difference < 1) { // The player lost money.
|
||||
return;
|
||||
}
|
||||
if (difference > 150_000_000l) {
|
||||
getDuplicationLog().flag(DuplicationLog.NW_INCREASE);
|
||||
getDuplicationLog().log("Large networth increase - [incr=" + difference + ", old=" + this.networth + ", cur=" + currentNet + "].", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the player does a mouse click.
|
||||
* @param x The x-coordinate of the cursor.
|
||||
* @param y The y-coordinate of the cursor.
|
||||
* @param delay The time between this click and last.
|
||||
* @param rightClick If the right mouse-button was used to click.
|
||||
*/
|
||||
public void handleMouseClick(int x, int y, int delay, boolean rightClick) {
|
||||
if (!clientFocus) {
|
||||
macroFlag |= MF_NO_FOCUS_CLICK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address log.
|
||||
* @param original the original log.
|
||||
* @param address the address.
|
||||
* @return the address.
|
||||
*/
|
||||
public static String getAddressLog(String original, String address) {
|
||||
String log = "";
|
||||
if (original != null && original.length() > 0) {
|
||||
log += original;
|
||||
if (log.charAt(log.length() - 1) != '|') {
|
||||
log += "|";
|
||||
}
|
||||
}
|
||||
if (address != null && address.length() > 0 && (original == null || !original.contains(address))) {
|
||||
log += address + "|";
|
||||
}
|
||||
if (log.length() > 0 && log.charAt(log.length() - 1) == '|') {
|
||||
log = log.substring(0, log.length() - 1);
|
||||
}
|
||||
if (log == null) {
|
||||
log = "";
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a macro flag.
|
||||
* @param flag The flag.
|
||||
*/
|
||||
public void addMacroFlag(int flag) {
|
||||
macroFlag |= flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macroFlag.
|
||||
* @return The macroFlag.
|
||||
*/
|
||||
public int getMacroFlag() {
|
||||
return macroFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the macroFlag.
|
||||
* @param macroFlag The macroFlag to set.
|
||||
*/
|
||||
public void setMacroFlag(int macroFlag) {
|
||||
this.macroFlag = macroFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the networth.
|
||||
* @return The networth.
|
||||
*/
|
||||
public long getNetworth() {
|
||||
return networth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the networth.
|
||||
* @param networth The networth to set.
|
||||
*/
|
||||
public void setNetworth(long networth) {
|
||||
this.networth = networth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the clientFocus.
|
||||
* @return The clientFocus.
|
||||
*/
|
||||
public boolean isClientFocus() {
|
||||
return clientFocus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the clientFocus.
|
||||
* @param clientFocus The clientFocus to set.
|
||||
*/
|
||||
public void setClientFocus(boolean clientFocus) {
|
||||
this.clientFocus = clientFocus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logs.
|
||||
* @return The logs.
|
||||
*/
|
||||
public MessageLog[] getLogs() {
|
||||
return logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duplicationLog.
|
||||
* @return The duplicationLog.
|
||||
*/
|
||||
public DuplicationLog getDuplicationLog() {
|
||||
if (duplicationLog == null) {
|
||||
duplicationLog = new DuplicationLog();
|
||||
}
|
||||
return duplicationLog;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package rs09.game.content.global.action
|
||||
|
||||
import api.events.PickUpEvent
|
||||
import api.getItemName
|
||||
import core.game.content.dialogue.FacialExpression
|
||||
import core.game.content.global.GodType
|
||||
import core.game.node.entity.player.Player
|
||||
@@ -13,6 +14,7 @@ import core.game.node.item.Item
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.config.GroundSpawnLoader
|
||||
import rs09.game.world.GameWorld
|
||||
@@ -55,6 +57,9 @@ object PickupHandler {
|
||||
return true
|
||||
}
|
||||
if (item.isActive && player.inventory.add(add)) {
|
||||
if (item.dropper is Player && item.dropper.details.uid != player.details.uid){
|
||||
PlayerMonitor.logMisc(item.dropper, "DropTrade", "${getItemName(item.id)} x${item.amount} picked up by ${player.name}.")
|
||||
}
|
||||
if (!RegionManager.isTeleportPermitted(item.location)) {
|
||||
player.animate(Animation.create(535))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
package rs09.game.node.entity.player.info
|
||||
|
||||
import api.getItemName
|
||||
import core.game.container.Container
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import discord.Discord
|
||||
import kotlinx.coroutines.*
|
||||
import org.sqlite.SQLiteDataSource
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.system.SystemLogger
|
||||
import java.io.File
|
||||
import java.sql.Connection
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import kotlin.math.abs
|
||||
|
||||
object PlayerMonitor {
|
||||
private val eventQueue = LinkedBlockingQueue<LogEvent>()
|
||||
private var activeTask: Job? = null
|
||||
|
||||
@JvmStatic fun logWealthChange(player: Player, totalWealth: Long, diff: Long) {
|
||||
val event = LogEvent.WealthLog(
|
||||
player.name,
|
||||
player.details.uid,
|
||||
totalWealth,
|
||||
diff,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
dispatch(event)
|
||||
if (abs(diff) >= 500_000L)
|
||||
Discord.postPlayerAlert(player.name, "Dubious change in wealth: $diff, total wealth: $totalWealth")
|
||||
}
|
||||
|
||||
@JvmStatic fun logChat(player: Player, type: String, message: String) {
|
||||
val event = LogEvent.ChatLog(
|
||||
player.name,
|
||||
player.details.uid,
|
||||
type,
|
||||
message,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
checkForFlaggedText(player.name, message)
|
||||
dispatch(event)
|
||||
}
|
||||
|
||||
@JvmStatic fun logTrade(player1: Player, player2: Player, container1: Container, container2: Container) {
|
||||
val container1String = StringBuilder()
|
||||
val container2String = StringBuilder()
|
||||
|
||||
for (item in container1.toArray()) {
|
||||
item ?: continue
|
||||
container1String.append(getItemName(item.id) + "(${item.amount}), ")
|
||||
}
|
||||
|
||||
for (item in container2.toArray()) {
|
||||
item ?: continue
|
||||
container2String.append(getItemName(item.id) + "(${item.amount}), ")
|
||||
}
|
||||
|
||||
val event = LogEvent.TradeLog(
|
||||
player1.name,
|
||||
player1.details.uid,
|
||||
player2.name,
|
||||
player2.details.uid,
|
||||
container1String.toString(),
|
||||
container2String.toString(),
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
dispatch(event)
|
||||
}
|
||||
|
||||
@JvmStatic fun logPrivateChat(sender: Player, receiver: String, message: String) {
|
||||
val event = LogEvent.ChatLog(
|
||||
sender.name,
|
||||
sender.details.uid,
|
||||
"private",
|
||||
"=> $receiver: $message",
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
checkForFlaggedText(sender.name, message)
|
||||
dispatch(event)
|
||||
}
|
||||
|
||||
@JvmStatic fun logMisc(player: Player, type: String, details: String) {
|
||||
val event = LogEvent.MiscLog(
|
||||
player.name,
|
||||
player.details.uid,
|
||||
type,
|
||||
details,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
dispatch(event)
|
||||
}
|
||||
|
||||
@JvmStatic fun logXpGains(player: Player, xpDiff: List<Pair<Int,Double>>) {
|
||||
if (player.isArtificial) return
|
||||
if (xpDiff.isEmpty()) return
|
||||
val query = StringBuilder("INSERT INTO xp_gains(player,uid,")
|
||||
val xpNames = StringBuilder()
|
||||
val xpAmounts = StringBuilder()
|
||||
for ((skillId, amount) in xpDiff) {
|
||||
xpNames.append(Skills.SKILL_NAME[skillId].lowercase() + ",")
|
||||
xpAmounts.append("$amount,")
|
||||
}
|
||||
query.append(xpNames)
|
||||
query.append("timestamp) VALUES(")
|
||||
query.append("'" + player.name + "',")
|
||||
query.append(player.details.uid.toString() + ",")
|
||||
query.append(xpAmounts)
|
||||
query.append(System.currentTimeMillis().toString())
|
||||
query.append(");")
|
||||
dispatch(LogEvent.XpLog(query.toString()))
|
||||
}
|
||||
|
||||
private fun dispatch(event: LogEvent) {
|
||||
eventQueue.put(event)
|
||||
if (eventQueue.size >= 50) {
|
||||
processQueuedEvents()
|
||||
}
|
||||
}
|
||||
|
||||
fun processQueuedEvents() {
|
||||
val path = ServerConstants.LOGS_PATH + "playerlogs.db"
|
||||
if (activeTask?.isActive == true)
|
||||
return
|
||||
if (!File(path).exists()) {
|
||||
createSqliteDatabase(path)
|
||||
}
|
||||
activeTask = GlobalScope.launch {
|
||||
val conn = connect(path)
|
||||
conn.use {
|
||||
while (eventQueue.isNotEmpty()) {
|
||||
val event = withContext(Dispatchers.IO) { eventQueue.take() }
|
||||
process(event, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun flushRemainingEventsImmediately() {
|
||||
SystemLogger.logInfo(this::class.java, "Flushing player log events...")
|
||||
val path = ServerConstants.LOGS_PATH + "playerlogs.db"
|
||||
if (!File(path).exists()) {
|
||||
createSqliteDatabase(path)
|
||||
}
|
||||
if (activeTask != null)
|
||||
activeTask?.cancel("Interrupted by shutdown. This is probably fine.")
|
||||
val conn = connect(path)
|
||||
conn.use {
|
||||
while (eventQueue.isNotEmpty())
|
||||
process(eventQueue.take(), it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun connect(path: String) : Connection {
|
||||
val source = SQLiteDataSource()
|
||||
source.url = "jdbc:sqlite:$path"
|
||||
return source.connection
|
||||
}
|
||||
|
||||
private fun createSqliteDatabase(path: String) {
|
||||
if (!File(ServerConstants.LOGS_PATH).exists())
|
||||
File(ServerConstants.LOGS_PATH).mkdirs()
|
||||
val connection = connect(path)
|
||||
val stmt = connection.createStatement()
|
||||
stmt.execute(
|
||||
"""
|
||||
CREATE TABLE "chat_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "message" TEXT, "timestamp" NUMERIC );
|
||||
""")
|
||||
stmt.execute(
|
||||
"""
|
||||
CREATE TABLE "misc_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "details" TEXT , "timestamp" NUMERIC);
|
||||
""")
|
||||
stmt.execute(
|
||||
"""
|
||||
CREATE TABLE "trade_logs" ( "player_a" TEXT, "player_b" TEXT, "uid_a" INTEGER, "uid_b" INTEGER, "items_a" TEXT, "items_b" TEXT, "timestamp" NUMERIC );
|
||||
""")
|
||||
stmt.execute(
|
||||
"""
|
||||
CREATE TABLE "xp_gains" ( "player" TEXT, "uid" INTEGER, "attack" INTEGER, "defence" INTEGER, "strength" INTEGER, "hitpoints" INTEGER, "ranged" INTEGER, "prayer" INTEGER, "magic" INTEGER, "cooking" INTEGER, "woodcutting" INTEGER, "fletching" INTEGER, "fishing" INTEGER, "firemaking" INTEGER, "crafting" INTEGER, "smithing" INTEGER, "mining" INTEGER, "herblore" INTEGER, "agility" INTEGER, "thieving" INTEGER, "slayer" INTEGER, "farming" INTEGER, "runecrafting" INTEGER, "hunter" INTEGER, "construction" INTEGER, "summoning" INTEGER , "timestamp" NUMERIC)
|
||||
""")
|
||||
stmt.execute(
|
||||
"""
|
||||
CREATE TABLE "wealth_logs" ( "player" TEXT, "uid" INTEGER, "total" NUMERIC, "diff" NUMERIC, "timestamp" NUMERIC )
|
||||
""")
|
||||
stmt.close()
|
||||
connection.close()
|
||||
}
|
||||
|
||||
private fun process(event: LogEvent, conn: Connection) {
|
||||
when (event) {
|
||||
is LogEvent.ChatLog -> {
|
||||
val stmt = conn.prepareStatement(CHAT_LOG_INSERT)
|
||||
stmt.setString(1, event.player)
|
||||
stmt.setInt(2, event.uid)
|
||||
stmt.setString(3, event.type)
|
||||
stmt.setString(4, event.message)
|
||||
stmt.setLong(5, event.timestamp)
|
||||
stmt.execute()
|
||||
}
|
||||
is LogEvent.TradeLog -> {
|
||||
val stmt = conn.prepareStatement(TRADE_LOG_INSERT)
|
||||
stmt.setString(1, event.player1)
|
||||
stmt.setString(2, event.player2)
|
||||
stmt.setInt(3, event.uid1)
|
||||
stmt.setInt(4, event.uid2)
|
||||
stmt.setString(5, event.items1)
|
||||
stmt.setString(6, event.items2)
|
||||
stmt.setLong(7, event.timestamp)
|
||||
stmt.execute()
|
||||
}
|
||||
is LogEvent.MiscLog -> {
|
||||
val stmt = conn.prepareStatement(MISC_LOG_INSERT)
|
||||
stmt.setString(1, event.player)
|
||||
stmt.setInt(2, event.uid)
|
||||
stmt.setString(3, event.type)
|
||||
stmt.setString(4, event.details)
|
||||
stmt.setLong(5, event.timestamp)
|
||||
stmt.execute()
|
||||
}
|
||||
is LogEvent.XpLog -> {
|
||||
val stmt = conn.createStatement()
|
||||
stmt.execute(event.query)
|
||||
}
|
||||
is LogEvent.WealthLog -> {
|
||||
val stmt = conn.prepareStatement(WEALTH_LOG_INSERT)
|
||||
stmt.setString(1, event.player)
|
||||
stmt.setInt(2, event.uid)
|
||||
stmt.setLong(3, event.total)
|
||||
stmt.setLong(4, event.diff)
|
||||
stmt.setLong(5, event.timeStamp)
|
||||
stmt.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForFlaggedText(username: String, message: String) {
|
||||
if (message.contains("dupe") || message.contains("bug") || message.contains("exploit") || message.contains("glitch")) {
|
||||
Discord.postPlayerAlert(username, "Flagged Chat - $message")
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LogEvent {
|
||||
data class ChatLog(val player: String, val uid: Int, val type: String, val message: String, val timestamp: Long) : LogEvent()
|
||||
data class TradeLog(val player1: String, val uid1: Int, val player2 : String, val uid2: Int, val items1: String, val items2: String, val timestamp: Long) : LogEvent()
|
||||
data class MiscLog(val player: String, val uid: Int, val type: String, val details: String, val timestamp: Long) : LogEvent()
|
||||
data class XpLog(val query: String) : LogEvent()
|
||||
data class WealthLog(val player: String, val uid: Int, val total: Long, val diff: Long, val timeStamp: Long) : LogEvent()
|
||||
}
|
||||
|
||||
private const val CHAT_LOG_INSERT = "INSERT INTO chat_logs(player,uid,type,message,timestamp) VALUES (?,?,?,?,?);"
|
||||
private const val TRADE_LOG_INSERT = "INSERT INTO trade_logs(player_a,player_b,uid_a,uid_b,items_a,items_b,timestamp) VALUES (?,?,?,?,?,?,?);"
|
||||
private const val MISC_LOG_INSERT = "INSERT INTO misc_logs(player,uid,type,details,timestamp) VALUES (?,?,?,?,?);"
|
||||
private const val WEALTH_LOG_INSERT = "INSERT INTO wealth_logs(player,uid,total,diff,timestamp) VALUES (?,?,?,?,?);"
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import core.game.node.entity.player.info.login.LoginConfiguration
|
||||
import core.game.node.entity.player.info.login.PlayerParser
|
||||
import core.game.node.entity.player.info.login.Response
|
||||
import core.game.system.SystemManager
|
||||
import core.game.system.monitor.PlayerMonitor
|
||||
import core.game.system.task.Pulse
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.world.GameWorld
|
||||
@@ -53,9 +52,6 @@ class LoginParser(val details: PlayerDetails) {
|
||||
flag(AuthResponse.Success)
|
||||
player.init()
|
||||
}
|
||||
player.monitor.log(player.details.ipAddress, PlayerMonitor.ADDRESS_LOG)
|
||||
player.monitor.log(player.details.serial, PlayerMonitor.ADDRESS_LOG)
|
||||
player.monitor.log(player.details.macAddress, PlayerMonitor.ADDRESS_LOG)
|
||||
} else {
|
||||
Repository.playerNames.remove(player.name)
|
||||
}
|
||||
|
||||
@@ -208,19 +208,6 @@ class PlayerSaveParser(val player: Player) {
|
||||
}
|
||||
|
||||
fun parseMonitor() {
|
||||
val monitorData = saveFile!!["playerMonitor"] as JSONObject
|
||||
if (monitorData.containsKey("duplicationFlag")) {
|
||||
val duplicationFlag: Int = (monitorData.get("duplicationFlag") as String).toInt()
|
||||
player.monitor.duplicationLog.flag(duplicationFlag)
|
||||
}
|
||||
if (monitorData.containsKey("macroFlag")) {
|
||||
val macroFlag: Int = (monitorData.get("macroFlag") as String).toInt()
|
||||
player.monitor.macroFlag = macroFlag
|
||||
}
|
||||
if (monitorData.containsKey("lastIncreaseFlag")) {
|
||||
val lastIncreaseFlag: Long = (monitorData.get("lastIncreaseFlag") as String).toLong()
|
||||
player.monitor.duplicationLog.lastIncreaseFlag = lastIncreaseFlag
|
||||
}
|
||||
}
|
||||
|
||||
fun parseConfigs() {
|
||||
|
||||
@@ -304,17 +304,7 @@ class PlayerSaver (val player: Player){
|
||||
}
|
||||
|
||||
fun savePlayerMonitor(root: JSONObject){
|
||||
val playerMonitor = JSONObject()
|
||||
if(player.monitor.duplicationLog != null && player.monitor.duplicationLog.flag != 0){
|
||||
playerMonitor.put("duplicationFlag",player.monitor.duplicationLog.flag.toString())
|
||||
}
|
||||
if(player.monitor.macroFlag != 0){
|
||||
playerMonitor.put("macroFlag",player.monitor.macroFlag.toString())
|
||||
}
|
||||
if(player.monitor.duplicationLog != null && player.monitor.duplicationLog.isLoggingFlagged){
|
||||
playerMonitor.put("lastIncreaseFlag",player.monitor.duplicationLog.lastIncreaseFlag.toString())
|
||||
}
|
||||
root.put("playerMonitor",playerMonitor)
|
||||
|
||||
}
|
||||
|
||||
fun saveConfigs(root: JSONObject){
|
||||
|
||||
@@ -29,7 +29,6 @@ import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.system.communication.ClanRank
|
||||
import core.game.system.communication.CommunicationInfo
|
||||
import core.game.system.monitor.PlayerMonitor
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
@@ -54,6 +53,7 @@ import rs09.game.interaction.InteractionListeners
|
||||
import rs09.game.interaction.InterfaceListeners
|
||||
import rs09.game.interaction.QCRepository
|
||||
import rs09.game.interaction.inter.ge.StockMarket
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor
|
||||
import rs09.game.node.entity.skill.magic.SpellListener
|
||||
import rs09.game.node.entity.skill.magic.SpellListeners
|
||||
import rs09.game.node.entity.skill.magic.SpellUtils
|
||||
@@ -199,8 +199,8 @@ object PacketProcessor {
|
||||
pkt.player.interfaceManager.closeChatbox()
|
||||
}
|
||||
is Packet.Command -> {
|
||||
if (CommandSystem.commandSystem.parse(pkt.player, pkt.commandLine))
|
||||
pkt.player.monitor.log(pkt.commandLine, PlayerMonitor.COMMAND_LOG)
|
||||
PlayerMonitor.logMisc(pkt.player, "CommandUse", pkt.commandLine)
|
||||
CommandSystem.commandSystem.parse(pkt.player, pkt.commandLine)
|
||||
}
|
||||
is Packet.ChatMessage -> {
|
||||
if (pkt.player.details.isMuted)
|
||||
@@ -215,7 +215,7 @@ object PacketProcessor {
|
||||
ManagementEvents.publish(builder.build())
|
||||
return
|
||||
}
|
||||
pkt.player.monitor.log(pkt.message, PlayerMonitor.PUBLIC_CHAT_LOG)
|
||||
PlayerMonitor.logChat(pkt.player, "public", pkt.message)
|
||||
val ctx = ChatMessage(pkt.player, pkt.message, pkt.effects, pkt.message.length)
|
||||
pkt.player.updateMasks.register(ChatFlag(ctx))
|
||||
}
|
||||
@@ -229,6 +229,7 @@ object PacketProcessor {
|
||||
pkt.player.sendMessage("You have been muted due to breaking a rule.")
|
||||
else
|
||||
CommunicationInfo.sendMessage(pkt.player, pkt.username, pkt.message)
|
||||
PlayerMonitor.logPrivateChat(pkt.player, pkt.username, pkt.message)
|
||||
}
|
||||
is Packet.PacketCountUpdate -> {
|
||||
val final = pkt.count - pkt.player.interfaceManager.getPacketCount(0)
|
||||
@@ -282,7 +283,7 @@ object PacketProcessor {
|
||||
|
||||
private fun processTrackingPacket(pkt: Packet) {
|
||||
when(pkt) {
|
||||
is Packet.TrackingFocus -> pkt.player.monitor.isClientFocus = pkt.isFocused
|
||||
is Packet.TrackingFocus -> {}
|
||||
is Packet.TrackingDisplayUpdate -> {
|
||||
pkt.player.session.clientInfo.screenWidth = pkt.screenWidth
|
||||
pkt.player.session.clientInfo.screenHeight = pkt.screenHeight
|
||||
@@ -298,9 +299,7 @@ object PacketProcessor {
|
||||
}
|
||||
is Packet.TrackingMouseClick -> {
|
||||
//TODO see above todo
|
||||
if (!pkt.player.monitor.isClientFocus) {
|
||||
Discord.postPlayerAlert(pkt.player.username, "Mouseclick without window focus!")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import rs09.ServerStore
|
||||
import rs09.ServerStore.Companion.addToList
|
||||
import rs09.ServerStore.Companion.getList
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.node.entity.player.info.PlayerMonitor
|
||||
import rs09.game.node.entity.player.info.login.LoginParser
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
@@ -131,6 +132,7 @@ object Login {
|
||||
}
|
||||
|
||||
val player = Player(details)
|
||||
PlayerMonitor.logMisc(player, "login_ip", details.ipAddress)
|
||||
if (canBypassAccountLimitCheck(player)) {
|
||||
proceedWithAcceptableLogin(session, player, opcode)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user