added FingerprintData class and improved device fingerprinting logic
This commit is contained in:
@@ -11,7 +11,6 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import spark.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -21,7 +20,7 @@ import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
public class DeviceFingerprinting extends Appliance {
|
||||
public record PackInfo(String url, UUID uuid, String hash) {
|
||||
public record PackInfo(@NotNull String url, @NotNull UUID uuid, @NotNull String hash) {
|
||||
private static final String failingUrl = "http://127.0.0.1:0";
|
||||
public PackInfo asFailing() {
|
||||
return new PackInfo(failingUrl, this.uuid, this.hash);
|
||||
@@ -29,14 +28,15 @@ public class DeviceFingerprinting extends Appliance {
|
||||
}
|
||||
|
||||
public enum PackStatus {
|
||||
ERROR,
|
||||
SUCCESS;
|
||||
UNCACHED,
|
||||
CACHED,
|
||||
INVALID;
|
||||
|
||||
public static PackStatus fromBukkitStatus(PlayerResourcePackStatusEvent.Status status) {
|
||||
return switch(status) {
|
||||
case DISCARDED -> SUCCESS;
|
||||
case FAILED_DOWNLOAD -> ERROR;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + status);
|
||||
case DISCARDED -> CACHED;
|
||||
case FAILED_DOWNLOAD -> UNCACHED;
|
||||
default -> INVALID;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -44,102 +44,60 @@ public class DeviceFingerprinting extends Appliance {
|
||||
public enum PlayerStatus {
|
||||
PREPARATION,
|
||||
TESTING,
|
||||
FINISHED;
|
||||
}
|
||||
|
||||
public record FingerprintData(
|
||||
Player player,
|
||||
PlayerStatus status,
|
||||
@Nullable Long fingerPrint,
|
||||
@Nullable List<PackStatus> pendingPacks
|
||||
) {
|
||||
public FingerprintData(Player player) {
|
||||
this(player, PlayerStatus.PREPARATION, null, null);
|
||||
}
|
||||
FINISHED,
|
||||
NEW
|
||||
}
|
||||
|
||||
private List<PackInfo> packs;
|
||||
private final Map<Player, List<PackStatus>> pendingPacks = new WeakHashMap<>();
|
||||
private final Map<Player, FingerprintData> fingerprints = new WeakHashMap<>();
|
||||
private final UUID basePackId = UUID.randomUUID();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.packs = this.readPacksFromConfig();
|
||||
|
||||
}
|
||||
|
||||
public void testAllPacks(Player player) {
|
||||
this.pendingPacks.put(player, Arrays.asList(new PackStatus[this.packs.size()]));
|
||||
System.out.println("Sending packs...");
|
||||
this.packs.forEach(pack -> this.sendPack(player, pack.asFailing()));
|
||||
public void startFingerprinting(Player player) {
|
||||
this.fingerprints.put(player, FingerprintData.create(player));
|
||||
Main.logger().info(String.format("Sending base ressource-pack with id '%s' to '%s'%n", this.basePackId, player.getName()));
|
||||
this.sendPack(player, new PackInfo("http://localhost:8080/api/devicefingerprinting/base.zip", this.basePackId, "3296e8bdd30b4f7cffd11c780a1dc70da2948e71"));
|
||||
}
|
||||
|
||||
public void onPackReceive(Player player, UUID packId, PlayerResourcePackStatusEvent.Status status) {
|
||||
if(!this.pendingPacks.containsKey(player)) return;
|
||||
public void onPackUpdate(Player player, UUID packId, PlayerResourcePackStatusEvent.Status status) {
|
||||
if(!this.fingerprints.containsKey(player)) return;
|
||||
FingerprintData playerFingerprint = this.fingerprints.get(player);
|
||||
if(!playerFingerprint.isInTestingOrPreparation()) return;
|
||||
|
||||
if(packId.equals(this.basePackId)) {
|
||||
Main.logger().info(String.format("Base pack for '%s' updated: '%s'", player.getName(), status));
|
||||
|
||||
if(status != PlayerResourcePackStatusEvent.Status.ACCEPTED) return;
|
||||
Main.logger().info(String.format("Base pack loaded successfully, sending now all packs to '%s'...", player.getName()));
|
||||
playerFingerprint.setTesting();
|
||||
this.packs.forEach(pack -> this.sendPack(player, pack.asFailing()));
|
||||
return;
|
||||
}
|
||||
|
||||
PackInfo pack = this.packs.stream()
|
||||
.filter(packInfo -> packInfo.uuid.equals(packId))
|
||||
.findFirst()
|
||||
.filter(packInfo -> Objects.equals(packInfo.uuid, packId))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
if(pack == null) return;
|
||||
int packIndex = this.packs.indexOf(pack);
|
||||
|
||||
List<PackStatus> currentPendingStatus = this.pendingPacks.get(player);
|
||||
try {
|
||||
currentPendingStatus.set(packIndex, PackStatus.fromBukkitStatus(status));
|
||||
System.out.println(packIndex + " > " + PackStatus.fromBukkitStatus(status));
|
||||
} catch(IllegalStateException ignored) {
|
||||
return;
|
||||
List<PackStatus> pendingPacks = playerFingerprint.getPendingPacks();
|
||||
PackStatus newPackStatus = PackStatus.fromBukkitStatus(status);
|
||||
if(newPackStatus == PackStatus.INVALID) return;
|
||||
pendingPacks.set(packIndex, newPackStatus);
|
||||
|
||||
playerFingerprint.updateFingerprint();
|
||||
if(Objects.requireNonNull(playerFingerprint.getStatus()) == PlayerStatus.NEW) {
|
||||
Main.logger().info(String.format("Sending fingerprint packs to Player '%s', as it is a unseen Player!", player.getName()));
|
||||
this.sendNewFingerprint(player, Objects.requireNonNull(playerFingerprint.getFingerPrint()));
|
||||
}
|
||||
|
||||
long fingerPrint = this.calculateFingerprintId(currentPendingStatus);
|
||||
if(fingerPrint == -1) return;
|
||||
|
||||
if(fingerPrint == 0) {
|
||||
// new Player
|
||||
long newFingerprintId = this.generateNewFingerprintId();
|
||||
System.out.println("New Fingerprint: " + newFingerprintId);
|
||||
this.sendMarkedPacks(player, newFingerprintId);
|
||||
} else {
|
||||
System.out.println("Fingerprint: " + fingerPrint);
|
||||
}
|
||||
|
||||
this.pendingPacks.remove(player);
|
||||
}
|
||||
|
||||
private long calculateFingerprintId(List<PackStatus> packStatus) {
|
||||
long fingerprintId = 0;
|
||||
for (int i = 0; i < packStatus.size(); i++) {
|
||||
var status = packStatus.get(i);
|
||||
if(status == null) return -1;
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
fingerprintId |= 1L << i;
|
||||
break;
|
||||
case ERROR:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return fingerprintId;
|
||||
}
|
||||
|
||||
private long generateNewFingerprintId() {
|
||||
long id = 0;
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < this.packs.size() / 2; i++) {
|
||||
while (true) {
|
||||
int bitIndex = random.nextInt(this.packs.size());
|
||||
if ((id & (1L << bitIndex)) == 0) {
|
||||
id |= 1L << bitIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private void sendMarkedPacks(Player player, long fingerprintId) {
|
||||
private void sendNewFingerprint(Player player, long fingerprintId) {
|
||||
for (int i = 0; i < this.packs.size(); i++) {
|
||||
if ((fingerprintId & (1L << i)) != 0) {
|
||||
PackInfo pack = this.packs.get(i);
|
||||
@@ -149,7 +107,6 @@ public class DeviceFingerprinting extends Appliance {
|
||||
}
|
||||
|
||||
public void sendPack(Player player, PackInfo pack) {
|
||||
System.out.println("Sending pack: " + pack.url);
|
||||
player.sendResourcePacks(
|
||||
ResourcePackRequest.resourcePackRequest()
|
||||
.required(true)
|
||||
@@ -161,12 +118,9 @@ public class DeviceFingerprinting extends Appliance {
|
||||
try (InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(Main.class.getResourceAsStream("/deviceFingerprinting/packs.json")))) {
|
||||
Type packListType = new TypeToken<List<DeviceFingerprinting.PackInfo>>(){}.getType();
|
||||
List<DeviceFingerprinting.PackInfo> packs = new Gson().fromJson(reader, packListType);
|
||||
|
||||
if (packs.isEmpty())
|
||||
throw new IllegalStateException("No resource packs found in packs.json.");
|
||||
|
||||
if (packs.isEmpty()) throw new IllegalStateException("No resource packs found in packs.json.");
|
||||
return packs;
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to parse packs.json.", e);
|
||||
}
|
||||
}
|
||||
@@ -209,6 +163,10 @@ public class DeviceFingerprinting extends Appliance {
|
||||
}
|
||||
}
|
||||
|
||||
public List<PackInfo> getPacks() {
|
||||
return this.packs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(
|
||||
|
@@ -0,0 +1,91 @@
|
||||
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
class FingerprintData {
|
||||
public final Player player;
|
||||
private DeviceFingerprinting.PlayerStatus status;
|
||||
private @Nullable Long fingerPrint;
|
||||
private final List<DeviceFingerprinting.PackStatus> pendingPacks;
|
||||
int packCount = Main.instance().getAppliance(DeviceFingerprinting.class).getPacks().size();
|
||||
|
||||
private FingerprintData(Player player) {
|
||||
this.player = player;
|
||||
this.status = DeviceFingerprinting.PlayerStatus.PREPARATION;
|
||||
this.fingerPrint = null;
|
||||
this.pendingPacks = Arrays.asList(new DeviceFingerprinting.PackStatus[this.packCount]);
|
||||
}
|
||||
|
||||
public static FingerprintData create(Player player) {
|
||||
return new FingerprintData(player);
|
||||
}
|
||||
|
||||
public void setTesting() {
|
||||
this.status = DeviceFingerprinting.PlayerStatus.TESTING;
|
||||
}
|
||||
|
||||
public void updateFingerprint() {
|
||||
long fingerPrint = 0;
|
||||
for (int i = 0; i < this.pendingPacks.size(); i++) {
|
||||
var status = this.pendingPacks.get(i);
|
||||
if(status == null) return;
|
||||
switch (status) {
|
||||
case CACHED:
|
||||
fingerPrint |= 1L << i;
|
||||
break;
|
||||
case UNCACHED:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(fingerPrint == 0) {
|
||||
this.status = DeviceFingerprinting.PlayerStatus.NEW;
|
||||
this.fingerPrint = this.createNewFingerprint();
|
||||
Main.logger().info(String.format("Player %s's was marked as a new Player!", this.player.getName()));
|
||||
} else {
|
||||
this.status = DeviceFingerprinting.PlayerStatus.FINISHED;
|
||||
this.fingerPrint = fingerPrint;
|
||||
}
|
||||
|
||||
Main.logger().info(String.format("Player %s's fingerprint is '%s'", this.player.getName(), fingerPrint));
|
||||
}
|
||||
|
||||
private long createNewFingerprint() {
|
||||
long id = 0;
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < this.packCount / 2; i++) {
|
||||
while (true) {
|
||||
int bitIndex = random.nextInt(this.packCount);
|
||||
if ((id & (1L << bitIndex)) == 0) {
|
||||
id |= 1L << bitIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<DeviceFingerprinting.PackStatus> getPendingPacks() {
|
||||
return this.pendingPacks;
|
||||
}
|
||||
|
||||
public DeviceFingerprinting.PlayerStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public @Nullable Long getFingerPrint() {
|
||||
return this.fingerPrint;
|
||||
}
|
||||
|
||||
public boolean isInTestingOrPreparation() {
|
||||
return this.status == DeviceFingerprinting.PlayerStatus.TESTING || this.status == DeviceFingerprinting.PlayerStatus.PREPARATION;
|
||||
}
|
||||
}
|
@@ -8,17 +8,12 @@ import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
||||
class PlayerJoinListener extends ApplianceListener<DeviceFingerprinting> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
// if(Bukkit.getServer().getServerResourcePack() != null && !event.getPlayer().hasResourcePack()) {
|
||||
// System.out.println("NO RESSOURCEPACK");
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.getAppliance().testAllPacks(event.getPlayer());
|
||||
this.getAppliance().startFingerprinting(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onResourcePackEvent(PlayerResourcePackStatusEvent event) {
|
||||
this.getAppliance().onPackReceive(
|
||||
this.getAppliance().onPackUpdate(
|
||||
event.getPlayer(),
|
||||
event.getID(),
|
||||
event.getStatus()
|
||||
|
Reference in New Issue
Block a user