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.Listener;
|
||||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import spark.Response;
|
import spark.Response;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -21,7 +20,7 @@ import java.net.URI;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class DeviceFingerprinting extends Appliance {
|
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";
|
private static final String failingUrl = "http://127.0.0.1:0";
|
||||||
public PackInfo asFailing() {
|
public PackInfo asFailing() {
|
||||||
return new PackInfo(failingUrl, this.uuid, this.hash);
|
return new PackInfo(failingUrl, this.uuid, this.hash);
|
||||||
@@ -29,14 +28,15 @@ public class DeviceFingerprinting extends Appliance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum PackStatus {
|
public enum PackStatus {
|
||||||
ERROR,
|
UNCACHED,
|
||||||
SUCCESS;
|
CACHED,
|
||||||
|
INVALID;
|
||||||
|
|
||||||
public static PackStatus fromBukkitStatus(PlayerResourcePackStatusEvent.Status status) {
|
public static PackStatus fromBukkitStatus(PlayerResourcePackStatusEvent.Status status) {
|
||||||
return switch(status) {
|
return switch(status) {
|
||||||
case DISCARDED -> SUCCESS;
|
case DISCARDED -> CACHED;
|
||||||
case FAILED_DOWNLOAD -> ERROR;
|
case FAILED_DOWNLOAD -> UNCACHED;
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + status);
|
default -> INVALID;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,102 +44,60 @@ public class DeviceFingerprinting extends Appliance {
|
|||||||
public enum PlayerStatus {
|
public enum PlayerStatus {
|
||||||
PREPARATION,
|
PREPARATION,
|
||||||
TESTING,
|
TESTING,
|
||||||
FINISHED;
|
FINISHED,
|
||||||
}
|
NEW
|
||||||
|
|
||||||
public record FingerprintData(
|
|
||||||
Player player,
|
|
||||||
PlayerStatus status,
|
|
||||||
@Nullable Long fingerPrint,
|
|
||||||
@Nullable List<PackStatus> pendingPacks
|
|
||||||
) {
|
|
||||||
public FingerprintData(Player player) {
|
|
||||||
this(player, PlayerStatus.PREPARATION, null, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PackInfo> packs;
|
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
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
this.packs = this.readPacksFromConfig();
|
this.packs = this.readPacksFromConfig();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllPacks(Player player) {
|
public void startFingerprinting(Player player) {
|
||||||
this.pendingPacks.put(player, Arrays.asList(new PackStatus[this.packs.size()]));
|
this.fingerprints.put(player, FingerprintData.create(player));
|
||||||
System.out.println("Sending packs...");
|
Main.logger().info(String.format("Sending base ressource-pack with id '%s' to '%s'%n", this.basePackId, player.getName()));
|
||||||
this.packs.forEach(pack -> this.sendPack(player, pack.asFailing()));
|
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) {
|
public void onPackUpdate(Player player, UUID packId, PlayerResourcePackStatusEvent.Status status) {
|
||||||
if(!this.pendingPacks.containsKey(player)) return;
|
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()
|
PackInfo pack = this.packs.stream()
|
||||||
.filter(packInfo -> packInfo.uuid.equals(packId))
|
.filter(packInfo -> Objects.equals(packInfo.uuid, packId))
|
||||||
.findFirst()
|
.findAny()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if(pack == null) return;
|
if(pack == null) return;
|
||||||
int packIndex = this.packs.indexOf(pack);
|
int packIndex = this.packs.indexOf(pack);
|
||||||
|
|
||||||
List<PackStatus> currentPendingStatus = this.pendingPacks.get(player);
|
List<PackStatus> pendingPacks = playerFingerprint.getPendingPacks();
|
||||||
try {
|
PackStatus newPackStatus = PackStatus.fromBukkitStatus(status);
|
||||||
currentPendingStatus.set(packIndex, PackStatus.fromBukkitStatus(status));
|
if(newPackStatus == PackStatus.INVALID) return;
|
||||||
System.out.println(packIndex + " > " + PackStatus.fromBukkitStatus(status));
|
pendingPacks.set(packIndex, newPackStatus);
|
||||||
} catch(IllegalStateException ignored) {
|
|
||||||
return;
|
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) {
|
private void sendNewFingerprint(Player player, long fingerprintId) {
|
||||||
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) {
|
|
||||||
for (int i = 0; i < this.packs.size(); i++) {
|
for (int i = 0; i < this.packs.size(); i++) {
|
||||||
if ((fingerprintId & (1L << i)) != 0) {
|
if ((fingerprintId & (1L << i)) != 0) {
|
||||||
PackInfo pack = this.packs.get(i);
|
PackInfo pack = this.packs.get(i);
|
||||||
@@ -149,7 +107,6 @@ public class DeviceFingerprinting extends Appliance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendPack(Player player, PackInfo pack) {
|
public void sendPack(Player player, PackInfo pack) {
|
||||||
System.out.println("Sending pack: " + pack.url);
|
|
||||||
player.sendResourcePacks(
|
player.sendResourcePacks(
|
||||||
ResourcePackRequest.resourcePackRequest()
|
ResourcePackRequest.resourcePackRequest()
|
||||||
.required(true)
|
.required(true)
|
||||||
@@ -161,12 +118,9 @@ public class DeviceFingerprinting extends Appliance {
|
|||||||
try (InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(Main.class.getResourceAsStream("/deviceFingerprinting/packs.json")))) {
|
try (InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(Main.class.getResourceAsStream("/deviceFingerprinting/packs.json")))) {
|
||||||
Type packListType = new TypeToken<List<DeviceFingerprinting.PackInfo>>(){}.getType();
|
Type packListType = new TypeToken<List<DeviceFingerprinting.PackInfo>>(){}.getType();
|
||||||
List<DeviceFingerprinting.PackInfo> packs = new Gson().fromJson(reader, packListType);
|
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;
|
return packs;
|
||||||
} catch (Exception e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Failed to parse packs.json.", 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
|
@Override
|
||||||
protected @NotNull List<Listener> listeners() {
|
protected @NotNull List<Listener> listeners() {
|
||||||
return List.of(
|
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> {
|
class PlayerJoinListener extends ApplianceListener<DeviceFingerprinting> {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onJoin(PlayerJoinEvent event) {
|
public void onJoin(PlayerJoinEvent event) {
|
||||||
// if(Bukkit.getServer().getServerResourcePack() != null && !event.getPlayer().hasResourcePack()) {
|
this.getAppliance().startFingerprinting(event.getPlayer());
|
||||||
// System.out.println("NO RESSOURCEPACK");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.getAppliance().testAllPacks(event.getPlayer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onResourcePackEvent(PlayerResourcePackStatusEvent event) {
|
public void onResourcePackEvent(PlayerResourcePackStatusEvent event) {
|
||||||
this.getAppliance().onPackReceive(
|
this.getAppliance().onPackUpdate(
|
||||||
event.getPlayer(),
|
event.getPlayer(),
|
||||||
event.getID(),
|
event.getID(),
|
||||||
event.getStatus()
|
event.getStatus()
|
||||||
|
Reference in New Issue
Block a user