291 lines
12 KiB
Java
291 lines
12 KiB
Java
/*
|
|
* This file is part of Player Analytics (Plan).
|
|
*
|
|
* Plan is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License v3 as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Plan is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package net.playeranalytics.plan.gathering.listeners.fabric;
|
|
|
|
import com.djrapitops.plan.delivery.domain.Nickname;
|
|
import com.djrapitops.plan.delivery.domain.PlayerName;
|
|
import com.djrapitops.plan.delivery.domain.ServerName;
|
|
import com.djrapitops.plan.delivery.export.Exporter;
|
|
import com.djrapitops.plan.extension.CallEvents;
|
|
import com.djrapitops.plan.extension.ExtensionSvc;
|
|
import com.djrapitops.plan.gathering.cache.NicknameCache;
|
|
import com.djrapitops.plan.gathering.cache.SessionCache;
|
|
import com.djrapitops.plan.gathering.domain.ActiveSession;
|
|
import com.djrapitops.plan.gathering.domain.event.JoinAddress;
|
|
import com.djrapitops.plan.gathering.geolocation.GeolocationCache;
|
|
import com.djrapitops.plan.identification.ServerInfo;
|
|
import com.djrapitops.plan.identification.ServerUUID;
|
|
import com.djrapitops.plan.processing.Processing;
|
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
|
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
|
|
import com.djrapitops.plan.settings.config.paths.ExportSettings;
|
|
import com.djrapitops.plan.storage.database.DBSystem;
|
|
import com.djrapitops.plan.storage.database.Database;
|
|
import com.djrapitops.plan.storage.database.transactions.events.*;
|
|
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
|
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
|
import com.google.common.net.InetAddresses;
|
|
import com.mojang.authlib.GameProfile;
|
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
|
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
|
import net.minecraft.server.network.ServerPlayerEntity;
|
|
import net.playeranalytics.plan.gathering.FabricPlayerPositionTracker;
|
|
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
|
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
|
|
|
import javax.inject.Inject;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketAddress;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.function.Supplier;
|
|
|
|
public class PlayerOnlineListener implements FabricListener {
|
|
|
|
private final PlanConfig config;
|
|
private final Processing processing;
|
|
private final ServerInfo serverInfo;
|
|
private final DBSystem dbSystem;
|
|
private final ExtensionSvc extensionService;
|
|
private final Exporter exporter;
|
|
private final GeolocationCache geolocationCache;
|
|
private final NicknameCache nicknameCache;
|
|
private final SessionCache sessionCache;
|
|
private final ErrorLogger errorLogger;
|
|
private final MinecraftDedicatedServer server;
|
|
|
|
private final Map<UUID, String> joinAddresses;
|
|
|
|
private boolean isEnabled = false;
|
|
|
|
@Inject
|
|
public PlayerOnlineListener(
|
|
PlanConfig config,
|
|
Processing processing,
|
|
ServerInfo serverInfo,
|
|
DBSystem dbSystem,
|
|
ExtensionSvc extensionService,
|
|
Exporter exporter,
|
|
GeolocationCache geolocationCache,
|
|
NicknameCache nicknameCache,
|
|
SessionCache sessionCache,
|
|
ErrorLogger errorLogger,
|
|
MinecraftDedicatedServer server
|
|
) {
|
|
this.config = config;
|
|
this.processing = processing;
|
|
this.serverInfo = serverInfo;
|
|
this.dbSystem = dbSystem;
|
|
this.extensionService = extensionService;
|
|
this.exporter = exporter;
|
|
this.geolocationCache = geolocationCache;
|
|
this.nicknameCache = nicknameCache;
|
|
this.sessionCache = sessionCache;
|
|
this.errorLogger = errorLogger;
|
|
this.server = server;
|
|
|
|
joinAddresses = new HashMap<>();
|
|
}
|
|
|
|
@Override
|
|
public void register() {
|
|
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
|
if (!this.isEnabled) {
|
|
return;
|
|
}
|
|
onPlayerJoin(handler.player);
|
|
});
|
|
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
|
if (!this.isEnabled) {
|
|
return;
|
|
}
|
|
onPlayerQuit(handler.player);
|
|
});
|
|
PlanFabricEvents.ON_KICKED.register((source, targets, reason) -> {
|
|
if (!this.isEnabled) {
|
|
return;
|
|
}
|
|
for (ServerPlayerEntity target : targets) {
|
|
onPlayerKick(target);
|
|
}
|
|
});
|
|
PlanFabricEvents.ON_LOGIN.register((address, profile, reason) -> {
|
|
if (!this.isEnabled) {
|
|
return;
|
|
}
|
|
onPlayerLogin(address, profile, reason != null);
|
|
});
|
|
this.enable();
|
|
}
|
|
|
|
public void onPlayerLogin(SocketAddress address, GameProfile profile, boolean banned) {
|
|
try {
|
|
UUID playerUUID = profile.getId();
|
|
ServerUUID serverUUID = serverInfo.getServerUUID();
|
|
String joinAddress = address.toString();
|
|
if (!joinAddress.isEmpty()) {
|
|
joinAddress = joinAddress.substring(0, joinAddress.lastIndexOf(':'));
|
|
joinAddresses.put(playerUUID, joinAddress);
|
|
dbSystem.getDatabase().executeTransaction(new StoreJoinAddressTransaction(joinAddress));
|
|
}
|
|
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> banned));
|
|
} catch (Exception e) {
|
|
errorLogger.error(e, ErrorContext.builder().related(getClass(), address, profile, banned).build());
|
|
}
|
|
}
|
|
|
|
public void onPlayerKick(ServerPlayerEntity player) {
|
|
try {
|
|
UUID uuid = player.getUuid();
|
|
if (FabricAFKListener.afkTracker.isAfk(uuid)) {
|
|
return;
|
|
}
|
|
|
|
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(uuid));
|
|
} catch (Exception e) {
|
|
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
|
}
|
|
}
|
|
|
|
public void onPlayerJoin(ServerPlayerEntity player) {
|
|
try {
|
|
actOnJoinEvent(player);
|
|
} catch (Exception e) {
|
|
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
|
}
|
|
}
|
|
|
|
private void actOnJoinEvent(ServerPlayerEntity player) {
|
|
|
|
UUID playerUUID = player.getUuid();
|
|
ServerUUID serverUUID = serverInfo.getServerUUID();
|
|
long time = System.currentTimeMillis();
|
|
|
|
FabricAFKListener.afkTracker.performedAction(playerUUID, time);
|
|
|
|
String world = player.getWorld().getRegistryKey().getValue().toString();
|
|
String gm = player.interactionManager.getGameMode().name();
|
|
|
|
Database database = dbSystem.getDatabase();
|
|
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
|
|
|
|
Supplier<String> getHostName = () -> getHostname(player);
|
|
|
|
String playerName = player.getEntityName();
|
|
String displayName = player.getDisplayName().getString();
|
|
|
|
|
|
database.executeTransaction(new PlayerServerRegisterTransaction(playerUUID,
|
|
System::currentTimeMillis, playerName, serverUUID, getHostName))
|
|
.thenRunAsync(() -> {
|
|
boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS);
|
|
if (gatheringGeolocations) {
|
|
gatherGeolocation(player, playerUUID, time, database);
|
|
}
|
|
|
|
database.executeTransaction(new OperatorStatusTransaction(playerUUID, serverUUID, server.getPlayerManager().getOpList().get(player.getGameProfile()) != null));
|
|
|
|
ActiveSession session = new ActiveSession(playerUUID, serverUUID, time, world, gm);
|
|
session.getExtraData().put(PlayerName.class, new PlayerName(playerName));
|
|
session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName()));
|
|
session.getExtraData().put(JoinAddress.class, new JoinAddress(getHostName));
|
|
sessionCache.cacheSession(playerUUID, session)
|
|
.ifPresent(previousSession -> database.executeTransaction(new StoreSessionTransaction(previousSession)));
|
|
|
|
database.executeTransaction(new NicknameStoreTransaction(
|
|
playerUUID, new Nickname(displayName, time, serverUUID),
|
|
(uuid, name) -> nicknameCache.getDisplayName(playerUUID).map(name::equals).orElse(false)
|
|
));
|
|
|
|
processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_JOIN));
|
|
if (config.isTrue(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) {
|
|
processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void gatherGeolocation(ServerPlayerEntity player, UUID playerUUID, long time, Database database) {
|
|
InetSocketAddress socketAddress = (InetSocketAddress) player.networkHandler.connection.getAddress();
|
|
if (socketAddress == null) return;
|
|
InetAddress address = InetAddresses.forString(socketAddress.getAddress().toString().replace("/", ""));
|
|
database.executeTransaction(
|
|
new StoreGeoInfoTransaction(playerUUID, address, time, geolocationCache::getCountry)
|
|
);
|
|
}
|
|
|
|
private String getHostname(ServerPlayerEntity player) {
|
|
return joinAddresses.get(player.getUuid());
|
|
}
|
|
|
|
// No event priorities on Fabric, so this has to be called with onPlayerQuit()
|
|
public void beforePlayerQuit(ServerPlayerEntity player) {
|
|
UUID playerUUID = player.getUuid();
|
|
String playerName = player.getEntityName();
|
|
processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_LEAVE));
|
|
}
|
|
|
|
public void onPlayerQuit(ServerPlayerEntity player) {
|
|
beforePlayerQuit(player);
|
|
try {
|
|
actOnQuitEvent(player);
|
|
FabricPlayerPositionTracker.removePlayer(player.getUuid());
|
|
} catch (Exception e) {
|
|
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
|
}
|
|
}
|
|
|
|
private void actOnQuitEvent(ServerPlayerEntity player) {
|
|
long time = System.currentTimeMillis();
|
|
String playerName = player.getEntityName();
|
|
UUID playerUUID = player.getUuid();
|
|
ServerUUID serverUUID = serverInfo.getServerUUID();
|
|
|
|
FabricAFKListener.afkTracker.loggedOut(playerUUID, time);
|
|
|
|
joinAddresses.remove(playerUUID);
|
|
nicknameCache.removeDisplayName(playerUUID);
|
|
|
|
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> server.getPlayerManager().getUserBanList().contains(player.getGameProfile())));
|
|
|
|
sessionCache.endSession(playerUUID, time)
|
|
.ifPresent(endedSession -> dbSystem.getDatabase().executeTransaction(new StoreSessionTransaction(endedSession)));
|
|
|
|
if (config.isTrue(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) {
|
|
processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isEnabled() {
|
|
return this.isEnabled;
|
|
}
|
|
|
|
@Override
|
|
public void enable() {
|
|
this.isEnabled = true;
|
|
}
|
|
|
|
@Override
|
|
public void disable() {
|
|
this.isEnabled = false;
|
|
}
|
|
|
|
|
|
}
|