Compare commits
7 Commits
develop-de
...
develop-an
Author | SHA1 | Date | |
---|---|---|---|
713561bf07 | |||
4be3e528b1 | |||
53fca580f3 | |||
20fb4bf9fb | |||
c42d259909 | |||
5c82c8d6da | |||
5910847172 |
@@ -1,176 +0,0 @@
|
|||||||
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
|
|
||||||
|
|
||||||
import com.google.common.reflect.TypeToken;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import eu.mhsl.craftattack.spawn.core.Main;
|
|
||||||
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
|
|
||||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
|
||||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
|
||||||
import net.kyori.adventure.resource.ResourcePackRequest;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import spark.Response;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class DeviceFingerprinting extends Appliance {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PackStatus {
|
|
||||||
UNCACHED,
|
|
||||||
CACHED,
|
|
||||||
INVALID;
|
|
||||||
|
|
||||||
public static PackStatus fromBukkitStatus(PlayerResourcePackStatusEvent.Status status) {
|
|
||||||
return switch(status) {
|
|
||||||
case DISCARDED -> CACHED;
|
|
||||||
case FAILED_DOWNLOAD -> UNCACHED;
|
|
||||||
default -> INVALID;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PlayerStatus {
|
|
||||||
PREPARATION,
|
|
||||||
TESTING,
|
|
||||||
FINISHED,
|
|
||||||
NEW
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<PackInfo> packs;
|
|
||||||
private final Map<Player, FingerprintData> fingerprints = new WeakHashMap<>();
|
|
||||||
private final UUID basePackId = UUID.randomUUID();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
this.packs = this.readPacksFromConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 -> Objects.equals(packInfo.uuid, packId))
|
|
||||||
.findAny()
|
|
||||||
.orElse(null);
|
|
||||||
if(pack == null) return;
|
|
||||||
int packIndex = this.packs.indexOf(pack);
|
|
||||||
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
this.sendPack(player, pack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendPack(Player player, PackInfo pack) {
|
|
||||||
player.sendResourcePacks(
|
|
||||||
ResourcePackRequest.resourcePackRequest()
|
|
||||||
.required(true)
|
|
||||||
.packs(ResourcePackInfo.resourcePackInfo(pack.uuid, URI.create(pack.url), pack.hash))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<DeviceFingerprinting.PackInfo> readPacksFromConfig() {
|
|
||||||
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.");
|
|
||||||
return packs;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException("Failed to parse packs.json.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
|
||||||
apiBuilder.rawGet(
|
|
||||||
"base.zip",
|
|
||||||
(request, response) -> this.servePack("base.zip", response)
|
|
||||||
);
|
|
||||||
|
|
||||||
for(int i = 0; i < this.packs.size(); i++) {
|
|
||||||
int packIndex = i;
|
|
||||||
apiBuilder.rawGet(
|
|
||||||
String.format("packs/%d", i),
|
|
||||||
(request, response) -> this.servePack(String.valueOf(packIndex), response)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object servePack(String name, Response response) {
|
|
||||||
try {
|
|
||||||
String resourcePath = String.format("/deviceFingerprinting/packs/%s", name);
|
|
||||||
var inputStream = Main.class.getResourceAsStream(resourcePath);
|
|
||||||
|
|
||||||
if (inputStream == null) {
|
|
||||||
throw new IllegalStateException("Pack file not found: " + resourcePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.header("Content-Type", "application/zip");
|
|
||||||
response.header("Content-Disposition", String.format("attachment; filename=\"pack-%s.zip\"", name));
|
|
||||||
|
|
||||||
var outputStream = response.raw().getOutputStream();
|
|
||||||
inputStream.transferTo(outputStream);
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
return HttpServer.nothing;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(String.format("Failed to serve pack '%s'", name), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PackInfo> getPacks() {
|
|
||||||
return this.packs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull List<Listener> listeners() {
|
|
||||||
return List.of(
|
|
||||||
new PlayerJoinListener()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,91 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
|
|
||||||
|
|
||||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
|
||||||
|
|
||||||
class PlayerJoinListener extends ApplianceListener<DeviceFingerprinting> {
|
|
||||||
@EventHandler
|
|
||||||
public void onJoin(PlayerJoinEvent event) {
|
|
||||||
this.getAppliance().startFingerprinting(event.getPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onResourcePackEvent(PlayerResourcePackStatusEvent event) {
|
|
||||||
this.getAppliance().onPackUpdate(
|
|
||||||
event.getPlayer(),
|
|
||||||
event.getID(),
|
|
||||||
event.getStatus()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +0,0 @@
|
|||||||
## Files originally from "TrackPack"
|
|
||||||
https://github.com/ALaggyDev/TrackPack/blob/main/README.md
|
|
||||||
|
|
||||||
|
|
||||||
Discovered by: [Laggy](https://github.com/ALaggyDev/) and [NikOverflow](https://github.com/NikOverflow)
|
|
@@ -1,33 +0,0 @@
|
|||||||
import zipfile
|
|
||||||
import hashlib
|
|
||||||
import uuid
|
|
||||||
import json
|
|
||||||
|
|
||||||
SERVER_URL = "http://localhost:8080/api/devicefingerprinting"
|
|
||||||
packs = []
|
|
||||||
|
|
||||||
def file_sha1(path):
|
|
||||||
h = hashlib.sha1()
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
for chunk in iter(lambda: f.read(8192), b""):
|
|
||||||
h.update(chunk)
|
|
||||||
return h.hexdigest()
|
|
||||||
|
|
||||||
for i in range(0, 24):
|
|
||||||
path = f"packs/{i}"
|
|
||||||
|
|
||||||
with zipfile.ZipFile(path, mode="w") as zf:
|
|
||||||
zf.writestr(
|
|
||||||
"pack.mcmeta",
|
|
||||||
'{"pack":{"pack_format":22,"supported_formats":[22,1000],"description":"pack ' + str(i) + '"}}',
|
|
||||||
)
|
|
||||||
|
|
||||||
hash = file_sha1(path)
|
|
||||||
packs.append({
|
|
||||||
"url": f"{SERVER_URL}/packs/{i}",
|
|
||||||
"uuid": str(uuid.uuid4()),
|
|
||||||
"hash": hash
|
|
||||||
})
|
|
||||||
|
|
||||||
with open("packs.json", "w") as f:
|
|
||||||
json.dump(packs, f, indent=4)
|
|
@@ -1,122 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/0",
|
|
||||||
"uuid": "b35f6e2f-1b50-4493-85be-fb18bd90f9bb",
|
|
||||||
"hash": "7a39af839ea6484431f7b707759546bea991d435"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/1",
|
|
||||||
"uuid": "71095b62-d5ef-4ab2-ba3b-3c1b403f5e34",
|
|
||||||
"hash": "a9192ee73df1c5cff2c188fac6e9e638a1e7b6ce"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/2",
|
|
||||||
"uuid": "a4dba0a2-f8f2-4a81-bbb2-a9a818820330",
|
|
||||||
"hash": "6b85b0eb54865dae70bbda89746d83717dc2a214"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/3",
|
|
||||||
"uuid": "79fa2dc4-8c84-45fc-a09f-d89906f0d900",
|
|
||||||
"hash": "c7abf7a316f7e8c98985e8317a8b649e824e9f79"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/4",
|
|
||||||
"uuid": "15702c9b-a22b-426d-b48a-3d65b0368e9a",
|
|
||||||
"hash": "10cd0e2c46f192deb87ac75c149827d44a713017"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/5",
|
|
||||||
"uuid": "3d702d41-8e2f-4920-8dd0-1fd2146da9fb",
|
|
||||||
"hash": "8ad517d259e800b88a38ff00ee6721d5656822f2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/6",
|
|
||||||
"uuid": "c20a2e47-ef43-49da-a80d-adf238df3695",
|
|
||||||
"hash": "798677405a4fd678892e1cf55585c8c91f82e1e2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/7",
|
|
||||||
"uuid": "7ce51b81-1263-4919-9f4e-bb749ffe6e2e",
|
|
||||||
"hash": "af473b8eb7572f35d307bede5f2e20f263c0d804"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/8",
|
|
||||||
"uuid": "0c70d586-fe48-4ffc-86b0-6b9ec3bfe045",
|
|
||||||
"hash": "2fb698ff88f2436637641f3b2e6792201feb5144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/9",
|
|
||||||
"uuid": "c7af75a8-0b72-495d-a0ff-c1c40e229c13",
|
|
||||||
"hash": "cf660460798eecf451d639873cc1fedc4661db1b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/10",
|
|
||||||
"uuid": "248dbce6-4b2a-44b5-b038-8d718b0ced99",
|
|
||||||
"hash": "a8ebe708d0f3747c76e4e5e68db5dcb561922706"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/11",
|
|
||||||
"uuid": "10979174-cb02-40eb-a754-275551ad608d",
|
|
||||||
"hash": "54961b48db1582a1a0981c8cc9be5ae0f3122cf3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/12",
|
|
||||||
"uuid": "a361cfa7-674c-4493-a4cf-4baff851f276",
|
|
||||||
"hash": "013719dc8da79c96b45a1c5319c20bffe1a56cc9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/13",
|
|
||||||
"uuid": "24b39bdb-ada9-40ec-9e3a-132c74b81dc6",
|
|
||||||
"hash": "206898c6b6600d2648b2d79c61fc6255b19587d9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/14",
|
|
||||||
"uuid": "158fc5b4-be2c-4f7a-98cb-af5993adcc90",
|
|
||||||
"hash": "061b266a7c526fb3a3152a4ea70ca5592e0b503c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/15",
|
|
||||||
"uuid": "4f9097a7-be02-48ad-919c-f292307f8490",
|
|
||||||
"hash": "45a667a0fe06246defabca14ef1271fb6db5a1ac"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/16",
|
|
||||||
"uuid": "3ce31e60-7e8a-4fb1-8c6d-da9065bea798",
|
|
||||||
"hash": "75bb12e46203d49e89aa9a826d267552372758bc"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/17",
|
|
||||||
"uuid": "cd978e5c-3de0-4ada-8ec5-3a88a305eec6",
|
|
||||||
"hash": "5b20261f7be03e83e9c52307f1408b0c5e58358c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/18",
|
|
||||||
"uuid": "75001e58-3999-4779-a1d1-43ab161770ce",
|
|
||||||
"hash": "544420cffb6c17113c06fb49eeba892c208719d3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/19",
|
|
||||||
"uuid": "6a7005a9-c2ca-476d-9a12-07d120ee121a",
|
|
||||||
"hash": "fcc066a4d3193b60b102e3d906ad8dc0b0fcf65b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/20",
|
|
||||||
"uuid": "521c0d84-d82e-49ef-b096-d9b90f15aa19",
|
|
||||||
"hash": "4545835983ec7f07d02675a69181a80dc396f038"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/21",
|
|
||||||
"uuid": "c1b590c5-43fc-41e3-83c0-47f35b14f845",
|
|
||||||
"hash": "8d4c670eaefc0482734e839b72758226dde13bc3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/22",
|
|
||||||
"uuid": "43958a18-c087-4f2b-a6ea-066231606eb1",
|
|
||||||
"hash": "004282602f7bdbb7cd7724f23aae23876f224092"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8080/api/devicefingerprinting/packs/23",
|
|
||||||
"uuid": "4b91ac81-9de4-4c2b-a876-47e621496d10",
|
|
||||||
"hash": "dae68eae109e08ea4c4c943905502eb331939f64"
|
|
||||||
}
|
|
||||||
]
|
|
@@ -1 +0,0 @@
|
|||||||
!base.zip
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,7 +7,6 @@ import org.bukkit.configuration.ConfigurationSection;
|
|||||||
import spark.Request;
|
import spark.Request;
|
||||||
import spark.Spark;
|
import spark.Spark;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -44,16 +43,12 @@ public class HttpServer {
|
|||||||
this.applianceName = appliance.getClass().getSimpleName().toLowerCase();
|
this.applianceName = appliance.getClass().getSimpleName().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rawGet(String path, BiFunction<Request, spark.Response, Object> onCall) {
|
|
||||||
Spark.get(this.buildRoute(path), (req, resp) -> this.process(() -> onCall.apply(req, resp)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void get(String path, Function<Request, Object> onCall) {
|
public void get(String path, Function<Request, Object> onCall) {
|
||||||
Spark.get(this.buildRoute(path), (req, resp) -> this.process(() -> onCall.apply(req)));
|
Spark.get(this.buildRoute(path), (req, resp) -> this.process(() -> onCall.apply(req)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rawPost(String path, BiFunction<Request, spark.Response, Object> onCall) {
|
public void rawPost(String path, Function<Request, Object> onCall) {
|
||||||
Spark.post(this.buildRoute(path), (req, resp) -> this.process(() -> onCall.apply(req, resp)));
|
Spark.post(this.buildRoute(path), (req, resp) -> this.process(() -> onCall.apply(req)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <TRequest> void post(String path, Class<TRequest> clazz, RequestProvider<TRequest, Request, Object> onCall) {
|
public <TRequest> void post(String path, Class<TRequest> clazz, RequestProvider<TRequest, Request, Object> onCall) {
|
||||||
|
@@ -0,0 +1,113 @@
|
|||||||
|
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.ironGolemAnimation;
|
||||||
|
|
||||||
|
import eu.mhsl.craftattack.spawn.core.Main;
|
||||||
|
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||||
|
import org.bukkit.*;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.Directional;
|
||||||
|
import org.bukkit.entity.IronGolem;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class IronGolemAnimation extends Appliance {
|
||||||
|
record BlockChange(Block original, BlockData fakeBlock) {}
|
||||||
|
|
||||||
|
public void onGolemSpawn(IronGolem golem) {
|
||||||
|
this.modifyGolem(golem, false);
|
||||||
|
Location golemLocation = golem.getLocation();
|
||||||
|
|
||||||
|
BlockData bodyBlockData = Bukkit.createBlockData(Material.IRON_BLOCK);
|
||||||
|
BlockData headBlockData = Bukkit.createBlockData(
|
||||||
|
Material.CARVED_PUMPKIN,
|
||||||
|
blockData -> ((Directional) blockData).setFacing(golem.getFacing())
|
||||||
|
);
|
||||||
|
Vector facingVector = golem.getFacing().getDirection().rotateAroundY(Math.toRadians(90));
|
||||||
|
Block golemCenterBlock = golemLocation.getBlock().getRelative(BlockFace.UP);
|
||||||
|
|
||||||
|
List<BlockChange> buildBlocks = List.of(
|
||||||
|
new BlockChange(golemCenterBlock.getRelative(BlockFace.DOWN), bodyBlockData),
|
||||||
|
new BlockChange(golemCenterBlock, bodyBlockData),
|
||||||
|
new BlockChange(golemCenterBlock.getLocation().add(facingVector).getBlock(), bodyBlockData),
|
||||||
|
new BlockChange(golemCenterBlock.getLocation().add(facingVector.multiply(-1)).getBlock(), bodyBlockData),
|
||||||
|
new BlockChange(golemCenterBlock.getRelative(BlockFace.UP), headBlockData)
|
||||||
|
);
|
||||||
|
|
||||||
|
Collection<Player> viewers = golemLocation.getNearbyPlayers(golemLocation.getWorld().getViewDistance() * 16);
|
||||||
|
BiConsumer<Location, BlockData> changeBlockForViewers = (location, blockData) -> {
|
||||||
|
viewers.forEach(player -> player.sendBlockChange(location, blockData));
|
||||||
|
golem.getWorld().playSound(
|
||||||
|
location,
|
||||||
|
blockData.getSoundGroup().getPlaceSound(),
|
||||||
|
SoundCategory.BLOCKS,
|
||||||
|
1f,
|
||||||
|
1f
|
||||||
|
);
|
||||||
|
};
|
||||||
|
for(int i = 0; i < buildBlocks.size(); i++) {
|
||||||
|
BlockChange blockChange = buildBlocks.get(i);
|
||||||
|
Bukkit.getScheduler().runTaskLater(
|
||||||
|
Main.instance(),
|
||||||
|
() -> changeBlockForViewers.accept(blockChange.original.getLocation(), blockChange.fakeBlock),
|
||||||
|
6L * i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Consumer<List<BlockChange>> restoreBlockChanges = (blocks) -> {
|
||||||
|
buildBlocks.forEach((blockChange) -> changeBlockForViewers.accept(
|
||||||
|
blockChange.original().getLocation(),
|
||||||
|
blockChange.original.getBlockData()
|
||||||
|
));
|
||||||
|
|
||||||
|
this.modifyGolem(golem, true);
|
||||||
|
this.spawnEffect(buildBlocks);
|
||||||
|
};
|
||||||
|
Bukkit.getScheduler().runTaskLater(
|
||||||
|
Main.instance(),
|
||||||
|
() -> restoreBlockChanges.accept(buildBlocks),
|
||||||
|
6L * buildBlocks.size() + 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spawnEffect(List<BlockChange> buildBlocks) {
|
||||||
|
buildBlocks.forEach((blockChange) -> {
|
||||||
|
World world = blockChange.original.getLocation().getWorld();
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.BLOCK,
|
||||||
|
blockChange.original.getLocation().add(0.5, 0.5, 0.5),
|
||||||
|
50,
|
||||||
|
blockChange.fakeBlock
|
||||||
|
);
|
||||||
|
world.playSound(
|
||||||
|
blockChange.original.getLocation(),
|
||||||
|
blockChange.fakeBlock.getSoundGroup().getBreakSound(),
|
||||||
|
SoundCategory.BLOCKS,
|
||||||
|
1f,
|
||||||
|
1f
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modifyGolem(IronGolem golem, boolean setVisible) {
|
||||||
|
golem.setInvisible(!setVisible);
|
||||||
|
golem.setInvulnerable(!setVisible);
|
||||||
|
golem.setAI(setVisible);
|
||||||
|
golem.setGravity(setVisible);
|
||||||
|
golem.setCollidable(setVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull List<Listener> listeners() {
|
||||||
|
return List.of(
|
||||||
|
new NaturalIronGolemSpawnEvent()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.ironGolemAnimation;
|
||||||
|
|
||||||
|
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||||
|
import org.bukkit.entity.IronGolem;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
|
||||||
|
class NaturalIronGolemSpawnEvent extends ApplianceListener<IronGolemAnimation> {
|
||||||
|
@EventHandler
|
||||||
|
public void onGolemSpawn(CreatureSpawnEvent event) {
|
||||||
|
if(!(event.getEntity() instanceof IronGolem golem)) return;
|
||||||
|
if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE) return;
|
||||||
|
this.getAppliance().onGolemSpawn(golem);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.recoveryCompass;
|
||||||
|
|
||||||
|
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
class KeepRecoveryCompassOnDeathListener extends ApplianceListener<RecoveryCompass> {
|
||||||
|
@EventHandler
|
||||||
|
public void onDeath(PlayerDeathEvent event) {
|
||||||
|
ItemStack source = event.getDrops().stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(item -> item.getType() == Material.RECOVERY_COMPASS)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (source == null) return;
|
||||||
|
|
||||||
|
if (source.getAmount() > 1) {
|
||||||
|
source.setAmount(source.getAmount() - 1);
|
||||||
|
} else {
|
||||||
|
event.getDrops().remove(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack kept = source.clone();
|
||||||
|
kept.setAmount(1);
|
||||||
|
event.getItemsToKeep().add(kept);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.recoveryCompass;
|
||||||
|
|
||||||
|
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.persistence.PersistentDataContainer;
|
||||||
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
|
||||||
|
class PlayerFirstJoinCompassGift extends ApplianceListener<RecoveryCompass> {
|
||||||
|
private final NamespacedKey alreadyGiftedKey = new NamespacedKey(this.getClass().getSimpleName().toLowerCase(), "alreadyGifted".toLowerCase());
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onJoin(PlayerJoinEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
PersistentDataContainer container = player.getPersistentDataContainer();
|
||||||
|
if(container.has(alreadyGiftedKey)) return;
|
||||||
|
player.getInventory().addItem(ItemStack.of(Material.RECOVERY_COMPASS));
|
||||||
|
container.set(alreadyGiftedKey, PersistentDataType.BOOLEAN, true);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.recoveryCompass;
|
||||||
|
|
||||||
|
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RecoveryCompass extends Appliance {
|
||||||
|
@Override
|
||||||
|
protected @NotNull List<Listener> listeners() {
|
||||||
|
return List.of(
|
||||||
|
new PlayerFirstJoinCompassGift(),
|
||||||
|
new KeepRecoveryCompassOnDeathListener()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
local.gradle.example
Normal file
24
local.gradle.example
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
tasks.register('deployVaroPlugin', Copy) {
|
||||||
|
dependsOn ":varo:shadowJar"
|
||||||
|
from { project(":varo").shadowJar.archivePath }
|
||||||
|
into file('path') // path to plugins folder
|
||||||
|
rename { fileName -> "varo.jar" }
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("uploadVaroPlugin") {
|
||||||
|
dependsOn(":varo:shadowJar")
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
def jarFile = project(":varo").tasks.named("shadowJar").get().outputs.files.singleFile
|
||||||
|
exec {
|
||||||
|
commandLine "scp", "-4", "-P", "22", jarFile.absolutePath, "user@host:path/varo.jar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('deployCraftAttackPlugin', Copy) {
|
||||||
|
dependsOn ":craftattack:shadowJar"
|
||||||
|
from { project(":craftattack").shadowJar.archivePath }
|
||||||
|
into file('path') // path to plugins folder
|
||||||
|
rename { fileName -> "craftattack.jar" }
|
||||||
|
}
|
@@ -1,49 +0,0 @@
|
|||||||
package eu.mhsl.craftattack.spawn.appliances.tooling.antiGrief;
|
|
||||||
|
|
||||||
import eu.mhsl.craftattack.spawn.appliance.Appliance;
|
|
||||||
import eu.mhsl.craftattack.spawn.appliances.tooling.antiGrief.player.PlayerGriefListener;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.entity.BlockDisplay;
|
|
||||||
import org.bukkit.entity.ItemDisplay;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.bukkit.util.Transformation;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class AntiGrief extends Appliance {
|
|
||||||
private final Map<Block, UUID> blockRegistry = new HashMap<>();
|
|
||||||
|
|
||||||
public void addTracking(Block block, UUID player) {
|
|
||||||
this.blockRegistry.put(block, player);
|
|
||||||
block.getLocation().getWorld().spawn(block.getLocation(), ItemDisplay.class, itemDisplay -> {
|
|
||||||
itemDisplay.setItemStack(ItemStack.of(Material.FIRE_CHARGE));
|
|
||||||
itemDisplay.teleport(block.getLocation().add(0.5, 0.5, 0.5));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable UUID getTracked(Block block) {
|
|
||||||
return this.blockRegistry.get(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDestroyed(Block block, UUID player) {
|
|
||||||
block.getLocation().getWorld().spawn(block.getLocation().add(0.5, 0.5, 0.5), BlockDisplay.class, blockDisplay -> {
|
|
||||||
blockDisplay.setBlock(Material.GOLD_BLOCK.createBlockData());
|
|
||||||
|
|
||||||
Transformation transformation = blockDisplay.getTransformation();
|
|
||||||
transformation.getScale().set(0.3);
|
|
||||||
blockDisplay.setTransformation(transformation);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull List<Listener> listeners() {
|
|
||||||
return List.of(new PlayerGriefListener());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,51 +0,0 @@
|
|||||||
package eu.mhsl.craftattack.spawn.appliances.tooling.antiGrief.player;
|
|
||||||
|
|
||||||
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
|
|
||||||
import eu.mhsl.craftattack.spawn.appliances.tooling.antiGrief.AntiGrief;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.block.BlockBurnEvent;
|
|
||||||
import org.bukkit.event.block.BlockIgniteEvent;
|
|
||||||
import org.bukkit.event.block.BlockSpreadEvent;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PlayerGriefListener extends ApplianceListener<AntiGrief> {
|
|
||||||
@EventHandler
|
|
||||||
public void onIgnite(BlockIgniteEvent event) {
|
|
||||||
Bukkit.broadcast(Component.text(event.getCause().toString()));
|
|
||||||
switch(event.getCause()) {
|
|
||||||
case LAVA:
|
|
||||||
case SPREAD:
|
|
||||||
case EXPLOSION:
|
|
||||||
if(event.getIgnitingBlock() == null) return;
|
|
||||||
UUID ignitedBy = this.getAppliance().getTracked(event.getIgnitingBlock());
|
|
||||||
this.getAppliance().addTracking(event.getBlock(), ignitedBy);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FLINT_AND_STEEL:
|
|
||||||
case ENDER_CRYSTAL:
|
|
||||||
case ARROW:
|
|
||||||
case FIREBALL:
|
|
||||||
if(event.getPlayer() == null) return;
|
|
||||||
this.getAppliance().addTracking(event.getBlock(), event.getPlayer().getUniqueId());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onSpread(BlockSpreadEvent event) {
|
|
||||||
if(!event.getBlock().getType().equals(Material.FIRE)) return;
|
|
||||||
UUID ignitedBy = this.getAppliance().getTracked(event.getBlock());
|
|
||||||
this.getAppliance().addTracking(event.getBlock(), ignitedBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onDestroy(BlockBurnEvent event) {
|
|
||||||
UUID ignitedBy = this.getAppliance().getTracked(event.getIgnitingBlock());
|
|
||||||
if(ignitedBy == null) return;
|
|
||||||
this.getAppliance().addDestroyed(event.getBlock(), ignitedBy);
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user