forked from SteamWar/SteamWar
Add SpigotCore module
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.10")
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":CommonCore"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.12")
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.player.PlayerLocaleChangeEvent;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class LocaleChangeWrapper12 implements LocaleChangeWrapper {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private void onLocale(PlayerLocaleChangeEvent event) {
|
||||||
|
SteamwarUser.get(event.getPlayer().getUniqueId()).setLocale(Locale.forLanguageTag(event.getLocale()), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_8"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_9"))
|
||||||
|
compileOnly(project(":CommonCore"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.14")
|
||||||
|
compileOnly("de.steamwar:worldedit:1.15")
|
||||||
|
}
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.SkullMeta;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper {
|
||||||
|
|
||||||
|
private static final Map<String, Material> renamedLegacy = new HashMap<>();
|
||||||
|
|
||||||
|
static{
|
||||||
|
renamedLegacy.put("WOOD", Material.OAK_WOOD);
|
||||||
|
renamedLegacy.put("SAPLING", Material.OAK_SAPLING);
|
||||||
|
renamedLegacy.put("STATIONARY_WATER", Material.WATER);
|
||||||
|
renamedLegacy.put("STATIONARY_LAVA", Material.LAVA);
|
||||||
|
renamedLegacy.put("LOG", Material.OAK_LOG);
|
||||||
|
renamedLegacy.put("LEAVES", Material.OAK_LEAVES);
|
||||||
|
renamedLegacy.put("BED_BLOCK", Material.RED_BED);
|
||||||
|
renamedLegacy.put("BED", Material.RED_BED);
|
||||||
|
renamedLegacy.put("PISTON_STICKY_BASE", Material.STICKY_PISTON);
|
||||||
|
renamedLegacy.put("WEB", Material.COBWEB);
|
||||||
|
renamedLegacy.put("LONG_GRASS", Material.TALL_GRASS);
|
||||||
|
renamedLegacy.put("PISTON_BASE", Material.PISTON);
|
||||||
|
renamedLegacy.put("PISTON_EXTENSION", Material.PISTON_HEAD);
|
||||||
|
renamedLegacy.put("WOOL", Material.WHITE_WOOL);
|
||||||
|
renamedLegacy.put("PISTON_MOVING_PIECE", Material.MOVING_PISTON);
|
||||||
|
renamedLegacy.put("YELLOW_FLOWER", Material.DANDELION);
|
||||||
|
renamedLegacy.put("RED_ROSE", Material.POPPY);
|
||||||
|
renamedLegacy.put("DOUBLE_STEP", Material.SMOOTH_STONE);
|
||||||
|
renamedLegacy.put("STEP", Material.SMOOTH_STONE_SLAB);
|
||||||
|
renamedLegacy.put("MOB_SPAWNER", Material.SPAWNER);
|
||||||
|
renamedLegacy.put("WOOD_STAIRS", Material.OAK_STAIRS);
|
||||||
|
renamedLegacy.put("WORKBENCH", Material.CRAFTING_TABLE);
|
||||||
|
renamedLegacy.put("CROPS", Material.WHEAT_SEEDS);
|
||||||
|
renamedLegacy.put("SEEDS", Material.WHEAT_SEEDS);
|
||||||
|
renamedLegacy.put("SOIL", Material.FARMLAND);
|
||||||
|
renamedLegacy.put("BURNING_FURNACE", Material.FURNACE);
|
||||||
|
renamedLegacy.put("SIGN_POST", Material.OAK_SIGN);
|
||||||
|
renamedLegacy.put("SIGN", Material.OAK_SIGN);
|
||||||
|
renamedLegacy.put("WOODEN_DOOR", Material.OAK_DOOR);
|
||||||
|
renamedLegacy.put("WOOD_DOOR", Material.OAK_DOOR);
|
||||||
|
renamedLegacy.put("RAILS", Material.RAIL);
|
||||||
|
renamedLegacy.put("WALL_SIGN", Material.OAK_WALL_SIGN);
|
||||||
|
renamedLegacy.put("STONE_PLATE", Material.STONE_PRESSURE_PLATE);
|
||||||
|
renamedLegacy.put("WOOD_PLATE", Material.OAK_PRESSURE_PLATE);
|
||||||
|
renamedLegacy.put("GLOWING_REDSTONE_ORE", Material.REDSTONE_ORE);
|
||||||
|
renamedLegacy.put("REDSTONE_TORCH_OFF", Material.REDSTONE_TORCH);
|
||||||
|
renamedLegacy.put("REDSTONE_TORCH_ON", Material.REDSTONE_TORCH);
|
||||||
|
renamedLegacy.put("IRON_DOOR_BLOCK", Material.IRON_DOOR);
|
||||||
|
renamedLegacy.put("SUGAR_CANE_BLOCK", Material.SUGAR_CANE);
|
||||||
|
renamedLegacy.put("CAKE_BLOCK", Material.CAKE);
|
||||||
|
renamedLegacy.put("MELON_BLOCK", Material.MELON);
|
||||||
|
renamedLegacy.put("BEETROOT_BLOCK", Material.BEETROOT);
|
||||||
|
renamedLegacy.put("FENCE", Material.OAK_FENCE);
|
||||||
|
renamedLegacy.put("PORTAL", Material.NETHER_PORTAL);
|
||||||
|
renamedLegacy.put("DIODE_BLOCK_OFF", Material.REPEATER);
|
||||||
|
renamedLegacy.put("DIODE_BLOCK_ON", Material.REPEATER);
|
||||||
|
renamedLegacy.put("DIODE", Material.REPEATER);
|
||||||
|
renamedLegacy.put("STAINED_GLASS", Material.WHITE_STAINED_GLASS);
|
||||||
|
renamedLegacy.put("TRAP_DOOR", Material.OAK_TRAPDOOR);
|
||||||
|
renamedLegacy.put("MONSTER_EGGS", Material.SKELETON_SPAWN_EGG);
|
||||||
|
renamedLegacy.put("MONSTER_EGG", Material.SKELETON_SPAWN_EGG);
|
||||||
|
renamedLegacy.put("SMOOTH_BRICK", Material.STONE_BRICKS);
|
||||||
|
renamedLegacy.put("HUGE_MUSHROOM_1", Material.MUSHROOM_STEM);
|
||||||
|
renamedLegacy.put("HUGE_MUSHROOM_2", Material.RED_MUSHROOM);
|
||||||
|
renamedLegacy.put("IRON_FENCE", Material.IRON_BARS);
|
||||||
|
renamedLegacy.put("THIN_GLASS", Material.GLASS_PANE);
|
||||||
|
renamedLegacy.put("FENCE_GATE", Material.OAK_FENCE_GATE);
|
||||||
|
renamedLegacy.put("SMOOTH_STAIRS", Material.STONE_BRICK_STAIRS);
|
||||||
|
renamedLegacy.put("MYCEL", Material.MYCELIUM);
|
||||||
|
renamedLegacy.put("WATER_LILY", Material.LILY_PAD);
|
||||||
|
renamedLegacy.put("NETHER_FENCE", Material.NETHER_BRICK_FENCE);
|
||||||
|
renamedLegacy.put("NETHER_WARTS", Material.NETHER_WART);
|
||||||
|
renamedLegacy.put("NETHER_STALK", Material.NETHER_WART);
|
||||||
|
renamedLegacy.put("ENCHANTMENT_TABLE", Material.ENCHANTING_TABLE);
|
||||||
|
renamedLegacy.put("ENDER_PORTAL", Material.END_PORTAL);
|
||||||
|
renamedLegacy.put("ENDER_PORTAL_FRAME", Material.END_PORTAL_FRAME);
|
||||||
|
renamedLegacy.put("ENDER_STONE", Material.END_STONE);
|
||||||
|
renamedLegacy.put("REDSTONE_LAMP_OFF", Material.REDSTONE_LAMP);
|
||||||
|
renamedLegacy.put("REDSTONE_LAMP_ON", Material.REDSTONE_LAMP);
|
||||||
|
renamedLegacy.put("WOOD_DOUBLE_STEP", Material.OAK_SLAB);
|
||||||
|
renamedLegacy.put("WOOD_STEP", Material.OAK_SLAB);
|
||||||
|
renamedLegacy.put("SPRUCE_WOOD_STAIRS", Material.SPRUCE_STAIRS);
|
||||||
|
renamedLegacy.put("BIRCH_WOOD_STAIRS", Material.BIRCH_STAIRS);
|
||||||
|
renamedLegacy.put("JUNGLE_WOOD_STAIRS", Material.JUNGLE_STAIRS);
|
||||||
|
renamedLegacy.put("COMMAND", Material.COMMAND_BLOCK);
|
||||||
|
renamedLegacy.put("COBBLE_WALL", Material.COBBLESTONE_WALL);
|
||||||
|
renamedLegacy.put("WOOD_BUTTON", Material.OAK_BUTTON);
|
||||||
|
renamedLegacy.put("SKULL", Material.SKELETON_SKULL);
|
||||||
|
renamedLegacy.put("SKULL_ITEM", Material.SKELETON_SKULL);
|
||||||
|
renamedLegacy.put("GOLD_PLATE", Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
|
||||||
|
renamedLegacy.put("IRON_PLATE", Material.HEAVY_WEIGHTED_PRESSURE_PLATE);
|
||||||
|
renamedLegacy.put("REDSTONE_COMPARATOR_OFF", Material.COMPARATOR);
|
||||||
|
renamedLegacy.put("REDSTONE_COMPARATOR_ON", Material.COMPARATOR);
|
||||||
|
renamedLegacy.put("REDSTONE_COMPARATOR", Material.COMPARATOR);
|
||||||
|
renamedLegacy.put("QUARTZ_ORE", Material.QUARTZ);
|
||||||
|
renamedLegacy.put("STAINED_CLAY", Material.WHITE_TERRACOTTA);
|
||||||
|
renamedLegacy.put("STAINED_GLASS_PANE", Material.WHITE_STAINED_GLASS_PANE);
|
||||||
|
renamedLegacy.put("LEAVES_2", Material.ACACIA_LEAVES);
|
||||||
|
renamedLegacy.put("LOG_2", Material.ACACIA_LOG);
|
||||||
|
renamedLegacy.put("CARPET", Material.WHITE_CARPET);
|
||||||
|
renamedLegacy.put("HARD_CLAY", Material.TERRACOTTA);
|
||||||
|
renamedLegacy.put("DOUBLE_PLANT", Material.SUNFLOWER);
|
||||||
|
renamedLegacy.put("STANDING_BANNER", Material.WHITE_BANNER);
|
||||||
|
renamedLegacy.put("BANNER", Material.WHITE_BANNER);
|
||||||
|
renamedLegacy.put("WALL_BANNER", Material.WHITE_WALL_BANNER);
|
||||||
|
renamedLegacy.put("DAYLIGHT_DETECTOR_INVERTED", Material.DAYLIGHT_DETECTOR);
|
||||||
|
renamedLegacy.put("DOUBLE_STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
|
||||||
|
renamedLegacy.put("STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
|
||||||
|
renamedLegacy.put("PURPUR_DOUBLE_SLAB", Material.PURPUR_SLAB);
|
||||||
|
renamedLegacy.put("END_BRICKS", Material.END_STONE_BRICKS);
|
||||||
|
renamedLegacy.put("COMMAND_REPEATING", Material.REPEATING_COMMAND_BLOCK);
|
||||||
|
renamedLegacy.put("COMMAND_CHAIN", Material.CHAIN_COMMAND_BLOCK);
|
||||||
|
renamedLegacy.put("MAGMA", Material.MAGMA_BLOCK);
|
||||||
|
renamedLegacy.put("RED_NETHER_BRICK", Material.RED_NETHER_BRICKS);
|
||||||
|
renamedLegacy.put("SILVER_SHULKER_BOX", Material.LIGHT_GRAY_SHULKER_BOX);
|
||||||
|
renamedLegacy.put("SILVER_GLAZED_TERRACOTTA", Material.LIGHT_GRAY_TERRACOTTA);
|
||||||
|
renamedLegacy.put("CONCRETE", Material.WHITE_CONCRETE);
|
||||||
|
renamedLegacy.put("CONCRETE_POWDER", Material.WHITE_CONCRETE_POWDER);
|
||||||
|
renamedLegacy.put("IRON_SPADE", Material.IRON_SHOVEL);
|
||||||
|
renamedLegacy.put("WOOD_SWORD", Material.WOODEN_SWORD);
|
||||||
|
renamedLegacy.put("WOOD_SPADE", Material.WOODEN_SHOVEL);
|
||||||
|
renamedLegacy.put("WOOD_PICKAXE", Material.WOODEN_PICKAXE);
|
||||||
|
renamedLegacy.put("WOOD_AXE", Material.WOODEN_AXE);
|
||||||
|
renamedLegacy.put("STONE_SPADE", Material.STONE_SHOVEL);
|
||||||
|
renamedLegacy.put("DIAMOND_SPADE", Material.DIAMOND_SHOVEL);
|
||||||
|
renamedLegacy.put("MUSHROOM_SOUP", Material.MUSHROOM_STEW);
|
||||||
|
renamedLegacy.put("GOLD_SWORD", Material.GOLDEN_SWORD);
|
||||||
|
renamedLegacy.put("GOLD_SPADE", Material.GOLDEN_SHOVEL);
|
||||||
|
renamedLegacy.put("GOLD_PICKAXE", Material.GOLDEN_PICKAXE);
|
||||||
|
renamedLegacy.put("GOLD_AXE", Material.GOLDEN_AXE);
|
||||||
|
renamedLegacy.put("SULPHUR", Material.GUNPOWDER);
|
||||||
|
renamedLegacy.put("WOOD_HOE", Material.WOODEN_HOE);
|
||||||
|
renamedLegacy.put("GOLD_HOE", Material.GOLDEN_HOE);
|
||||||
|
renamedLegacy.put("GOLD_HELMET", Material.GOLDEN_HELMET);
|
||||||
|
renamedLegacy.put("GOLD_CHESTPLATE", Material.GOLDEN_CHESTPLATE);
|
||||||
|
renamedLegacy.put("GOLD_LEGGINGS", Material.GOLDEN_LEGGINGS);
|
||||||
|
renamedLegacy.put("GOLD_BOOTS", Material.GOLDEN_BOOTS);
|
||||||
|
renamedLegacy.put("PORK", Material.PORKCHOP);
|
||||||
|
renamedLegacy.put("GRILLED_PORK", Material.COOKED_PORKCHOP);
|
||||||
|
renamedLegacy.put("SNOW_BALL", Material.SNOWBALL);
|
||||||
|
renamedLegacy.put("BOAT", Material.OAK_BOAT);
|
||||||
|
renamedLegacy.put("CLAY_BRICK", Material.BRICKS);
|
||||||
|
renamedLegacy.put("STORAGE_MINECART", Material.CHEST_MINECART);
|
||||||
|
renamedLegacy.put("POWERED_MINECART", Material.FURNACE_MINECART);
|
||||||
|
renamedLegacy.put("WATCH", Material.CLOCK);
|
||||||
|
renamedLegacy.put("RAW_FISH", Material.SALMON);
|
||||||
|
renamedLegacy.put("COOKED_FISH", Material.COOKED_SALMON);
|
||||||
|
renamedLegacy.put("INK_SACK", Material.INK_SAC);
|
||||||
|
renamedLegacy.put("RAW_BEEF", Material.BEEF);
|
||||||
|
renamedLegacy.put("RAW_CHICKEN", Material.CHICKEN);
|
||||||
|
renamedLegacy.put("EYE_OF_ENDER", Material.ENDER_EYE);
|
||||||
|
renamedLegacy.put("SPECKLED_MELON", Material.GLISTERING_MELON_SLICE);
|
||||||
|
renamedLegacy.put("EXP_BOTTLE", Material.EXPERIENCE_BOTTLE);
|
||||||
|
renamedLegacy.put("FIREBALL", Material.FIRE_CHARGE);
|
||||||
|
renamedLegacy.put("BOOK_AND_QUILL", Material.WRITABLE_BOOK);
|
||||||
|
renamedLegacy.put("FLOWER_POT_ITEM", Material.FLOWER_POT);
|
||||||
|
renamedLegacy.put("EMPTY_MAP", Material.MAP);
|
||||||
|
renamedLegacy.put("BREWING_STAND_ITEM", Material.BREWING_STAND);
|
||||||
|
renamedLegacy.put("CAULDRON_ITEM", Material.CAULDRON);
|
||||||
|
renamedLegacy.put("CARROT_ITEM", Material.CARROT);
|
||||||
|
renamedLegacy.put("POTATO_ITEM", Material.POTATO);
|
||||||
|
renamedLegacy.put("SPRUCE_DOOR_ITEM", Material.SPRUCE_DOOR);
|
||||||
|
renamedLegacy.put("BIRCH_DOOR_ITEM", Material.BIRCH_DOOR);
|
||||||
|
renamedLegacy.put("JUNGLE_DOOR_ITEM", Material.JUNGLE_DOOR);
|
||||||
|
renamedLegacy.put("ACACIA_DOOR_ITEM", Material.ACACIA_DOOR);
|
||||||
|
renamedLegacy.put("DARK_OAK_DOOR_ITEM", Material.DARK_OAK_DOOR);
|
||||||
|
renamedLegacy.put("CARROT_STICK", Material.CARROT_ON_A_STICK);
|
||||||
|
renamedLegacy.put("FIREWORK", Material.FIREWORK_ROCKET);
|
||||||
|
renamedLegacy.put("FIREWORK_CHARGE", Material.FIREWORK_STAR);
|
||||||
|
renamedLegacy.put("NETHER_BRICK_ITEM", Material.NETHER_BRICKS);
|
||||||
|
renamedLegacy.put("EXPLOSIVE_MINECART", Material.TNT_MINECART);
|
||||||
|
renamedLegacy.put("IRON_BARDING", Material.IRON_HORSE_ARMOR);
|
||||||
|
renamedLegacy.put("GOLD_BARDING", Material.GOLDEN_HORSE_ARMOR);
|
||||||
|
renamedLegacy.put("DIAMOND_BARDING", Material.DIAMOND_HORSE_ARMOR);
|
||||||
|
renamedLegacy.put("LEASH", Material.LEAD);
|
||||||
|
renamedLegacy.put("COMMAND_MINECART", Material.COMMAND_BLOCK_MINECART);
|
||||||
|
renamedLegacy.put("CHORUS_FRUIT_POPPED", Material.POPPED_CHORUS_FRUIT);
|
||||||
|
renamedLegacy.put("DRAGONS_BREATH", Material.DRAGON_BREATH);
|
||||||
|
renamedLegacy.put("BOAT_SPRUCE", Material.SPRUCE_BOAT);
|
||||||
|
renamedLegacy.put("BOAT_BIRCH", Material.BIRCH_BOAT);
|
||||||
|
renamedLegacy.put("BOAT_JUNGLE", Material.JUNGLE_BOAT);
|
||||||
|
renamedLegacy.put("BOAT_ACACIA", Material.ACACIA_BOAT);
|
||||||
|
renamedLegacy.put("BOAT_DARK_OAK", Material.DARK_OAK_BOAT);
|
||||||
|
renamedLegacy.put("TOTEM", Material.TOTEM_OF_UNDYING);
|
||||||
|
renamedLegacy.put("GOLD_RECORD", Material.MUSIC_DISC_13);
|
||||||
|
renamedLegacy.put("GREEN_RECORD", Material.MUSIC_DISC_CAT);
|
||||||
|
renamedLegacy.put("RECORD_3", Material.MUSIC_DISC_BLOCKS);
|
||||||
|
renamedLegacy.put("RECORD_4", Material.MUSIC_DISC_CHIRP);
|
||||||
|
renamedLegacy.put("RECORD_5", Material.MUSIC_DISC_FAR);
|
||||||
|
renamedLegacy.put("RECORD_6", Material.MUSIC_DISC_MALL);
|
||||||
|
renamedLegacy.put("RECORD_7", Material.MUSIC_DISC_MELLOHI);
|
||||||
|
renamedLegacy.put("RECORD_8", Material.MUSIC_DISC_STAL);
|
||||||
|
renamedLegacy.put("RECORD_9", Material.MUSIC_DISC_STRAD);
|
||||||
|
renamedLegacy.put("RECORD_10", Material.MUSIC_DISC_WARD);
|
||||||
|
renamedLegacy.put("RECORD_11", Material.MUSIC_DISC_11);
|
||||||
|
renamedLegacy.put("RECORD_12", Material.MUSIC_DISC_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<?> scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, Reflection.getClass("{nms.network.chat}.IChatBaseComponent"), 0);
|
||||||
|
@Override
|
||||||
|
public void setScoreboardTitle(Object packet, String title) {
|
||||||
|
scoreboardName.set(packet, ChatWrapper.impl.stringToChatComponent(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> scoreActionEnum = Reflection.getClass("{nms.server}.ScoreboardServer$Action");
|
||||||
|
private static final Reflection.FieldAccessor<?> scoreAction = Reflection.getField(FlatteningWrapper.scoreboardScore, scoreActionEnum, 0);
|
||||||
|
private static final Object scoreActionChange = scoreActionEnum.getEnumConstants()[0];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setScoreAction(Object packet) {
|
||||||
|
scoreAction.set(packet, scoreActionChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Material getMaterial(String material) {
|
||||||
|
try{
|
||||||
|
return Material.valueOf(material);
|
||||||
|
}catch(IllegalArgumentException e){
|
||||||
|
return renamedLegacy.get(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Material getDye(int colorCode) {
|
||||||
|
switch(colorCode){
|
||||||
|
case 1:
|
||||||
|
return Material.RED_DYE;
|
||||||
|
case 2:
|
||||||
|
return Material.GREEN_DYE;
|
||||||
|
case 3:
|
||||||
|
return Material.BROWN_DYE;
|
||||||
|
case 4:
|
||||||
|
return Material.LAPIS_LAZULI;
|
||||||
|
case 5:
|
||||||
|
return Material.PURPLE_DYE;
|
||||||
|
case 6:
|
||||||
|
return Material.CYAN_DYE;
|
||||||
|
case 7:
|
||||||
|
return Material.LIGHT_GRAY_DYE;
|
||||||
|
case 8:
|
||||||
|
return Material.GRAY_DYE;
|
||||||
|
case 9:
|
||||||
|
return Material.PINK_DYE;
|
||||||
|
case 10:
|
||||||
|
return Material.LIME_DYE;
|
||||||
|
case 11:
|
||||||
|
return Material.YELLOW_DYE;
|
||||||
|
case 12:
|
||||||
|
return Material.LIGHT_BLUE_DYE;
|
||||||
|
case 13:
|
||||||
|
return Material.MAGENTA_DYE;
|
||||||
|
case 14:
|
||||||
|
return Material.ORANGE_DYE;
|
||||||
|
case 15:
|
||||||
|
return Material.WHITE_DYE;
|
||||||
|
default:
|
||||||
|
return Material.BLACK_DYE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public ItemStack setSkullOwner(String player) {
|
||||||
|
ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1);
|
||||||
|
SkullMeta headmeta = (SkullMeta) head.getItemMeta();
|
||||||
|
assert headmeta != null;
|
||||||
|
headmeta.setOwningPlayer(Bukkit.getOfflinePlayer(player.startsWith(".") ? player.substring(1) : player));
|
||||||
|
headmeta.setDisplayName(player);
|
||||||
|
head.setItemMeta(headmeta);
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> entityPose = Reflection.getClass("{nms.world.entity}.EntityPose");
|
||||||
|
private static final Object standing = entityPose.getEnumConstants()[0];
|
||||||
|
private static final Object swimming = entityPose.getEnumConstants()[3];
|
||||||
|
private static final Object sneaking = entityPose.getEnumConstants()[5];
|
||||||
|
@Override
|
||||||
|
public Object getPose(FlatteningWrapper.EntityPose pose) {
|
||||||
|
switch (pose) {
|
||||||
|
case SNEAKING:
|
||||||
|
return sneaking;
|
||||||
|
case SWIMMING:
|
||||||
|
return swimming;
|
||||||
|
case NORMAL:
|
||||||
|
default:
|
||||||
|
return standing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNamedSpawnPacketDataWatcher(Object packet) {
|
||||||
|
// field not present
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object formatDisplayName(String displayName) {
|
||||||
|
return displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> registryBlocks = Reflection.getClass("{nms.core}.RegistryBlocks");
|
||||||
|
private static final Class<?> entityTypes = Reflection.getClass("{nms.world.entity}.EntityTypes");
|
||||||
|
private static final Object entityTypesRegistry = Reflection.getField(Reflection.getClass(Core.getVersion() > 18 ? "net.minecraft.core.registries.BuiltInRegistries" : "{nms.core}.IRegistry"), registryBlocks, 0, entityTypes).get(null);
|
||||||
|
private static final Reflection.MethodInvoker get = Reflection.getMethod(registryBlocks, null, Reflection.getClass("{nms.resources}.MinecraftKey"));
|
||||||
|
private static final Reflection.FieldAccessor<?> spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0);
|
||||||
|
private static final Reflection.FieldAccessor<?> spawnLivingType = Core.getVersion() > 18 ? spawnType : Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
|
||||||
|
private static final Reflection.MethodInvoker toMinecraft = Reflection.getMethod("{obc}.util.CraftNamespacedKey", "toMinecraft", NamespacedKey.class);
|
||||||
|
private static final Map<EntityType, Object> types = new HashMap<>();
|
||||||
|
static {
|
||||||
|
types.put(EntityType.ARMOR_STAND, 1);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setSpawnPacketType(Object packet, EntityType type) {
|
||||||
|
if(type.isAlive()) {
|
||||||
|
spawnLivingType.set(packet, Core.getVersion() > 18 ? get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())) : types.get(type));
|
||||||
|
} else {
|
||||||
|
spawnType.set(packet, get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getViewDistance(Player player) {
|
||||||
|
return player.getClientViewDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftWorld", "getHandle");
|
||||||
|
private static final Reflection.MethodInvoker save = Reflection.getMethod("{nms.server.level}.WorldServer", null, Reflection.getClass("{nms.util}.IProgressUpdate"), boolean.class, boolean.class);
|
||||||
|
@Override
|
||||||
|
public void syncSave(World world) {
|
||||||
|
save.invoke(getHandle.invoke(world), null, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.player.PlayerRecipeDiscoverEvent;
|
||||||
|
|
||||||
|
public class RecipeDiscoverWrapper14 implements RecipeDiscoverWrapper {
|
||||||
|
@EventHandler
|
||||||
|
public void onRecipeDiscover(PlayerRecipeDiscoverEvent e) {
|
||||||
|
e.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,610 @@
|
|||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.sk89q.jnbt.*;
|
||||||
|
import com.sk89q.worldedit.EmptyClipboardException;
|
||||||
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||||
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
|
import com.sk89q.worldedit.extension.platform.Capability;
|
||||||
|
import com.sk89q.worldedit.extension.platform.Platform;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.*;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.*;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
|
import com.sk89q.worldedit.world.DataFixer;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||||
|
import de.steamwar.sql.NoClipboardException;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
public class WorldEditWrapper14 implements WorldEditWrapper.IWorldEditWrapper {
|
||||||
|
|
||||||
|
private static final ClipboardFormat SCHEMATIC = ClipboardFormats.findByAlias("schematic");
|
||||||
|
private static final ClipboardFormat SCHEM = ClipboardFormats.findByAlias("schem");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
|
||||||
|
ClipboardHolder clipboardHolder;
|
||||||
|
try {
|
||||||
|
clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard();
|
||||||
|
} catch (EmptyClipboardException e) {
|
||||||
|
throw new NoClipboardException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Clipboard clipboard = clipboardHolder.getClipboard();
|
||||||
|
if(clipboard == null)
|
||||||
|
throw new NoClipboardException();
|
||||||
|
|
||||||
|
PipedOutputStream outputStream = new PipedOutputStream();
|
||||||
|
PipedInputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = new PipedInputStream(outputStream, 4096);
|
||||||
|
}catch(NullPointerException e){
|
||||||
|
throw new RuntimeException(e.getMessage(), new IOException(e));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Could not init piped input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try{
|
||||||
|
if(schemFormat){
|
||||||
|
ClipboardWriter writer = SCHEM.getWriter(outputStream);
|
||||||
|
writer.write(clipboard);
|
||||||
|
writer.close();
|
||||||
|
}else{
|
||||||
|
SCHEMATIC.getWriter(outputStream).write(clipboard);
|
||||||
|
}
|
||||||
|
}catch(NullPointerException | IOException e) {
|
||||||
|
Core.getInstance().getLogger().log(Level.SEVERE, "Could not write schematic", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Core.getInstance().getLogger().log(Level.SEVERE, "Could not close schem writer", e);
|
||||||
|
}
|
||||||
|
}, "SchemWriter").start();
|
||||||
|
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) {
|
||||||
|
Clipboard clipboard = null;
|
||||||
|
try {
|
||||||
|
clipboard = getClipboard(is, schemFormat);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipboard == null)
|
||||||
|
throw new NoClipboardException();
|
||||||
|
|
||||||
|
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
|
||||||
|
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||||
|
try {
|
||||||
|
if(schemFormat){
|
||||||
|
return new SpongeSchematicReader(new NBTInputStream(is)).read();
|
||||||
|
}else{
|
||||||
|
return new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||||
|
}
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new NoClipboardException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MCEditSchematicReader extends NBTSchematicReader {
|
||||||
|
|
||||||
|
private final NBTInputStream inputStream;
|
||||||
|
private final DataFixer fixer;
|
||||||
|
private boolean faweSchem = false;
|
||||||
|
private static final ImmutableList<NBTCompatibilityHandler> COMPATIBILITY_HANDLERS
|
||||||
|
= ImmutableList.of(
|
||||||
|
new SignCompatibilityHandler(),
|
||||||
|
new FlowerPotCompatibilityHandler(),
|
||||||
|
new NoteBlockCompatibilityHandler(),
|
||||||
|
new SkullBlockCompatibilityHandler()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param inputStream the input stream to read from
|
||||||
|
*/
|
||||||
|
MCEditSchematicReader(NBTInputStream inputStream) {
|
||||||
|
checkNotNull(inputStream);
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.fixer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clipboard read() throws IOException {
|
||||||
|
// Schematic tag
|
||||||
|
NamedTag rootTag = inputStream.readNamedTag();
|
||||||
|
if (!rootTag.getName().equals("Schematic")) {
|
||||||
|
throw new IOException("Tag 'Schematic' does not exist or is not first");
|
||||||
|
}
|
||||||
|
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
|
||||||
|
|
||||||
|
// Check
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
if (!schematic.containsKey("Blocks")) {
|
||||||
|
throw new IOException("Schematic file is missing a 'Blocks' tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check type of Schematic
|
||||||
|
String materials = requireTag(schematic, "Materials", StringTag.class).getValue();
|
||||||
|
if (!materials.equals("Alpha")) {
|
||||||
|
throw new IOException("Schematic file is not an Alpha schematic");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// Metadata
|
||||||
|
// ====================================================================
|
||||||
|
|
||||||
|
BlockVector3 origin;
|
||||||
|
Region region;
|
||||||
|
|
||||||
|
// Get information
|
||||||
|
short width = requireTag(schematic, "Width", ShortTag.class).getValue();
|
||||||
|
short height = requireTag(schematic, "Height", ShortTag.class).getValue();
|
||||||
|
short length = requireTag(schematic, "Length", ShortTag.class).getValue();
|
||||||
|
|
||||||
|
int originX = 0;
|
||||||
|
int originY = 0;
|
||||||
|
int originZ = 0;
|
||||||
|
try {
|
||||||
|
originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue();
|
||||||
|
originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue();
|
||||||
|
originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue();
|
||||||
|
BlockVector3 min = BlockVector3.at(originX, originY, originZ);
|
||||||
|
|
||||||
|
int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue();
|
||||||
|
int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue();
|
||||||
|
int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue();
|
||||||
|
BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ);
|
||||||
|
|
||||||
|
origin = min.subtract(offset);
|
||||||
|
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
origin = BlockVector3.ZERO;
|
||||||
|
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// Blocks
|
||||||
|
// ====================================================================
|
||||||
|
|
||||||
|
// Get blocks
|
||||||
|
byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue();
|
||||||
|
byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue();
|
||||||
|
byte[] addId = new byte[0];
|
||||||
|
short[] blocks = new short[blockId.length]; // Have to later combine IDs
|
||||||
|
|
||||||
|
// We support 4096 block IDs using the same method as vanilla Minecraft, where
|
||||||
|
// the highest 4 bits are stored in a separate byte array.
|
||||||
|
if (schematic.containsKey("AddBlocks")) {
|
||||||
|
addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the AddBlocks data with the first 8-bit block ID
|
||||||
|
for (int index = 0; index < blockId.length; index++) {
|
||||||
|
if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
|
||||||
|
blocks[index] = (short) (blockId[index] & 0xFF);
|
||||||
|
} else {
|
||||||
|
if ((index & 1) == 0) {
|
||||||
|
blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
|
||||||
|
} else {
|
||||||
|
blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to pull out tile entities
|
||||||
|
final ListTag tileEntityTag = getTag(schematic, "TileEntities", ListTag.class);
|
||||||
|
List<Tag> tileEntities = tileEntityTag == null ? new ArrayList<>() : tileEntityTag.getValue();
|
||||||
|
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
|
||||||
|
Map<BlockVector3, BlockState> blockStates = new HashMap<>();
|
||||||
|
|
||||||
|
for (Tag tag : tileEntities) {
|
||||||
|
if (!(tag instanceof CompoundTag)) continue;
|
||||||
|
CompoundTag t = (CompoundTag) tag;
|
||||||
|
int x = t.getInt("x");
|
||||||
|
int y = t.getInt("y");
|
||||||
|
int z = t.getInt("z");
|
||||||
|
int index = y * width * length + z * width + x;
|
||||||
|
if(index < 0 || index >= blocks.length)
|
||||||
|
faweSchem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Tag tag : tileEntities) {
|
||||||
|
if (!(tag instanceof CompoundTag)) continue;
|
||||||
|
CompoundTag t = (CompoundTag) tag;
|
||||||
|
Map<String, Tag> values = new HashMap<>(t.getValue());
|
||||||
|
String id = t.getString("id");
|
||||||
|
values.put("id", new StringTag(convertBlockEntityId(id)));
|
||||||
|
int x = t.getInt("x");
|
||||||
|
int y = t.getInt("y");
|
||||||
|
int z = t.getInt("z");
|
||||||
|
if(faweSchem){
|
||||||
|
x -= originX;
|
||||||
|
y -= originY;
|
||||||
|
z -= originZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = y * width * length + z * width + x;
|
||||||
|
|
||||||
|
try{
|
||||||
|
BlockState block = getBlockState(blocks[index], blockData[index]);
|
||||||
|
BlockState newBlock = block;
|
||||||
|
if (newBlock != null) {
|
||||||
|
for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) {
|
||||||
|
if (handler.isAffectedBlock(newBlock)) {
|
||||||
|
newBlock = handler.updateNBT(block, values);
|
||||||
|
if (newBlock == null || values.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
t = null;
|
||||||
|
} else {
|
||||||
|
t = new CompoundTag(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixer != null && t != null) {
|
||||||
|
t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockVector3 vec = BlockVector3.at(x, y, z);
|
||||||
|
if (t != null) {
|
||||||
|
tileEntitiesMap.put(vec, t.getValue());
|
||||||
|
}
|
||||||
|
blockStates.put(vec, newBlock);
|
||||||
|
}catch(ArrayIndexOutOfBoundsException e){
|
||||||
|
//ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
|
||||||
|
clipboard.setOrigin(origin);
|
||||||
|
|
||||||
|
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int z = 0; z < length; ++z) {
|
||||||
|
int index = y * width * length + z * width + x;
|
||||||
|
BlockVector3 pt = BlockVector3.at(x, y, z);
|
||||||
|
BlockState state = blockStates.computeIfAbsent(pt, p -> getBlockState(blocks[index], blockData[index]));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (state != null) {
|
||||||
|
if (tileEntitiesMap.containsKey(pt)) {
|
||||||
|
clipboard.setBlock(region.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt))));
|
||||||
|
} else {
|
||||||
|
clipboard.setBlock(region.getMinimumPoint().add(pt), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clipboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String convertBlockEntityId(String id) {
|
||||||
|
switch (id) {
|
||||||
|
case "Cauldron":
|
||||||
|
return "brewing_stand";
|
||||||
|
case "Control":
|
||||||
|
return "command_block";
|
||||||
|
case "DLDetector":
|
||||||
|
return "daylight_detector";
|
||||||
|
case "Trap":
|
||||||
|
return "dispenser";
|
||||||
|
case "EnchantTable":
|
||||||
|
return "enchanting_table";
|
||||||
|
case "EndGateway":
|
||||||
|
return "end_gateway";
|
||||||
|
case "AirPortal":
|
||||||
|
return "end_portal";
|
||||||
|
case "EnderChest":
|
||||||
|
return "ender_chest";
|
||||||
|
case "FlowerPot":
|
||||||
|
return "flower_pot";
|
||||||
|
case "RecordPlayer":
|
||||||
|
return "jukebox";
|
||||||
|
case "MobSpawner":
|
||||||
|
return "mob_spawner";
|
||||||
|
case "Music":
|
||||||
|
case "noteblock":
|
||||||
|
return "note_block";
|
||||||
|
case "Structure":
|
||||||
|
return "structure_block";
|
||||||
|
case "Chest":
|
||||||
|
return "chest";
|
||||||
|
case "Sign":
|
||||||
|
return "sign";
|
||||||
|
case "Banner":
|
||||||
|
return "banner";
|
||||||
|
case "Beacon":
|
||||||
|
return "beacon";
|
||||||
|
case "Comparator":
|
||||||
|
return "comparator";
|
||||||
|
case "Dropper":
|
||||||
|
return "dropper";
|
||||||
|
case "Furnace":
|
||||||
|
return "furnace";
|
||||||
|
case "Hopper":
|
||||||
|
return "hopper";
|
||||||
|
case "Skull":
|
||||||
|
return "skull";
|
||||||
|
default:
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getBlockState(int id, int data) {
|
||||||
|
return LegacyMapper.getInstance().getBlockFromLegacy(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static class SpongeSchematicReader extends NBTSchematicReader {
|
||||||
|
|
||||||
|
private final NBTInputStream inputStream;
|
||||||
|
private DataFixer fixer = null;
|
||||||
|
private int schematicVersion = -1;
|
||||||
|
private int dataVersion = -1;
|
||||||
|
private boolean faweSchem = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param inputStream the input stream to read from
|
||||||
|
*/
|
||||||
|
public SpongeSchematicReader(NBTInputStream inputStream) {
|
||||||
|
checkNotNull(inputStream);
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clipboard read() throws IOException {
|
||||||
|
CompoundTag schematicTag = getBaseTag();
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
|
||||||
|
final Platform platform = WorldEdit.getInstance().getPlatformManager()
|
||||||
|
.queryCapability(Capability.WORLD_EDITING);
|
||||||
|
int liveDataVersion = platform.getDataVersion();
|
||||||
|
|
||||||
|
if (schematicVersion == 1) {
|
||||||
|
dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1-
|
||||||
|
fixer = platform.getDataFixer();
|
||||||
|
return readVersion1(schematicTag);
|
||||||
|
} else if (schematicVersion == 2) {
|
||||||
|
dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue();
|
||||||
|
if (dataVersion < liveDataVersion) {
|
||||||
|
fixer = platform.getDataFixer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return readVersion1(schematicTag);
|
||||||
|
}
|
||||||
|
throw new IOException("This schematic version is currently not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptionalInt getDataVersion() {
|
||||||
|
try {
|
||||||
|
CompoundTag schematicTag = getBaseTag();
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
if (schematicVersion == 1) {
|
||||||
|
return OptionalInt.of(1631);
|
||||||
|
} else if (schematicVersion == 2) {
|
||||||
|
return OptionalInt.of(requireTag(schematic, "DataVersion", IntTag.class).getValue());
|
||||||
|
}
|
||||||
|
return OptionalInt.empty();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return OptionalInt.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompoundTag getBaseTag() throws IOException {
|
||||||
|
NamedTag rootTag = inputStream.readNamedTag();
|
||||||
|
if (!rootTag.getName().equals("Schematic")) {
|
||||||
|
throw new IOException("Tag 'Schematic' does not exist or is not first");
|
||||||
|
}
|
||||||
|
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
|
||||||
|
|
||||||
|
// Check
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
|
||||||
|
schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue();
|
||||||
|
return schematicTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOException {
|
||||||
|
BlockVector3 origin;
|
||||||
|
Region region;
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
|
||||||
|
int width = requireTag(schematic, "Width", ShortTag.class).getValue();
|
||||||
|
int height = requireTag(schematic, "Height", ShortTag.class).getValue();
|
||||||
|
int length = requireTag(schematic, "Length", ShortTag.class).getValue();
|
||||||
|
|
||||||
|
IntArrayTag offsetTag = getTag(schematic, "Offset", IntArrayTag.class);
|
||||||
|
int[] offsetParts;
|
||||||
|
if (offsetTag != null) {
|
||||||
|
offsetParts = offsetTag.getValue();
|
||||||
|
if (offsetParts.length != 3) {
|
||||||
|
throw new IOException("Invalid offset specified in schematic.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offsetParts = new int[] {0, 0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockVector3 min = BlockVector3.at(offsetParts[0], offsetParts[1], offsetParts[2]);
|
||||||
|
|
||||||
|
CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class);
|
||||||
|
int offsetX = 0;
|
||||||
|
int offsetY = 0;
|
||||||
|
int offsetZ = 0;
|
||||||
|
if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) {
|
||||||
|
// We appear to have WorldEdit Metadata
|
||||||
|
Map<String, Tag> metadata = metadataTag.getValue();
|
||||||
|
offsetX = requireTag(metadata, "WEOffsetX", IntTag.class).getValue();
|
||||||
|
offsetY = requireTag(metadata, "WEOffsetY", IntTag.class).getValue();
|
||||||
|
offsetZ = requireTag(metadata, "WEOffsetZ", IntTag.class).getValue();
|
||||||
|
BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ);
|
||||||
|
origin = min.subtract(offset);
|
||||||
|
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE));
|
||||||
|
} else {
|
||||||
|
origin = min;
|
||||||
|
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class);
|
||||||
|
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
|
||||||
|
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) {
|
||||||
|
throw new IOException("Block palette size does not match expected size.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, BlockState> palette = new HashMap<>();
|
||||||
|
|
||||||
|
ParserContext parserContext = new ParserContext();
|
||||||
|
parserContext.setRestricted(false);
|
||||||
|
parserContext.setTryLegacy(false);
|
||||||
|
parserContext.setPreferringWildcard(false);
|
||||||
|
|
||||||
|
for (String palettePart : paletteObject.keySet()) {
|
||||||
|
int id = requireTag(paletteObject, palettePart, IntTag.class).getValue();
|
||||||
|
if (fixer != null) {
|
||||||
|
palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion);
|
||||||
|
}
|
||||||
|
BlockState state;
|
||||||
|
try {
|
||||||
|
state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState();
|
||||||
|
} catch (InputParseException e) {
|
||||||
|
state = BlockTypes.AIR.getDefaultState();
|
||||||
|
}
|
||||||
|
palette.put(id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue();
|
||||||
|
|
||||||
|
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
|
||||||
|
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class);
|
||||||
|
if (tileEntities == null) {
|
||||||
|
tileEntities = getTag(schematic, "TileEntities", ListTag.class);
|
||||||
|
}
|
||||||
|
if (tileEntities != null) {
|
||||||
|
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream()
|
||||||
|
.map(tag -> (CompoundTag) tag)
|
||||||
|
.map(CompoundTag::getValue)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (Map<String, Tag> tileEntity : tileEntityTags) {
|
||||||
|
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
|
||||||
|
if(pos[0] < 0 || pos[0] >= width || pos[1] < 0 || pos[1] >= height || pos[2] < 0 || pos[2] >= length)
|
||||||
|
faweSchem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map<String, Tag> tileEntity : tileEntityTags) {
|
||||||
|
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
|
||||||
|
final BlockVector3 pt = BlockVector3.at(pos[0], pos[1], pos[2]);
|
||||||
|
Map<String, Tag> values = Maps.newHashMap(tileEntity);
|
||||||
|
if(faweSchem){
|
||||||
|
values.put("x", new IntTag(pt.getBlockX() - offsetX));
|
||||||
|
values.put("y", new IntTag(pt.getBlockY() - offsetY));
|
||||||
|
values.put("z", new IntTag(pt.getBlockZ() - offsetZ));
|
||||||
|
}else{
|
||||||
|
values.put("x", new IntTag(pt.getBlockX()));
|
||||||
|
values.put("y", new IntTag(pt.getBlockY()));
|
||||||
|
values.put("z", new IntTag(pt.getBlockZ()));
|
||||||
|
}
|
||||||
|
values.put("id", values.get("Id"));
|
||||||
|
values.remove("Id");
|
||||||
|
values.remove("Pos");
|
||||||
|
if (fixer != null) {
|
||||||
|
tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, new CompoundTag(values), dataVersion).getValue();
|
||||||
|
} else {
|
||||||
|
tileEntity = values;
|
||||||
|
}
|
||||||
|
tileEntitiesMap.put(pt, tileEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
|
||||||
|
clipboard.setOrigin(origin);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
int i = 0;
|
||||||
|
int value;
|
||||||
|
int varintLength;
|
||||||
|
while (i < blocks.length) {
|
||||||
|
value = 0;
|
||||||
|
varintLength = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
value |= (blocks[i] & 127) << (varintLength++ * 7);
|
||||||
|
if (varintLength > 5) {
|
||||||
|
throw new IOException("VarInt too big (probably corrupted data)");
|
||||||
|
}
|
||||||
|
if ((blocks[i] & 128) != 128) {
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// index = (y * length * width) + (z * width) + x
|
||||||
|
int y = index / (width * length);
|
||||||
|
int z = (index % (width * length)) / width;
|
||||||
|
int x = (index % (width * length)) % width;
|
||||||
|
BlockState state = palette.get(value);
|
||||||
|
BlockVector3 pt = BlockVector3.at(x, y, z);
|
||||||
|
try {
|
||||||
|
if (tileEntitiesMap.containsKey(pt)) {
|
||||||
|
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt))));
|
||||||
|
} else {
|
||||||
|
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state);
|
||||||
|
}
|
||||||
|
} catch (WorldEditException e) {
|
||||||
|
throw new IOException("Failed to load a block in the schematic");
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clipboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BlockIds14 implements BlockIds {
|
||||||
|
|
||||||
|
private static final Class<?> blockStateList = Reflection.getClass("{nms.world.level.block.state}.BlockStateList");
|
||||||
|
private static final Class<?> fluidTypeFlowing = Reflection.getClass("{nms.world.level.material}.FluidTypeFlowing");
|
||||||
|
private static final Class<?> fluid = Reflection.getClass("{nms.world.level.material}.Fluid");
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getBlockData = Reflection.getTypedMethod(TechHider.block, null, TechHider.iBlockData);
|
||||||
|
@Override
|
||||||
|
public int materialToId(Material material) {
|
||||||
|
return getCombinedId(getBlockData.invoke(getBlock(material)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getStates = Reflection.getTypedMethod(TechHider.block, null, blockStateList);
|
||||||
|
private static final Reflection.MethodInvoker getStateList = Reflection.getTypedMethod(blockStateList, null, ImmutableList.class);
|
||||||
|
private static final Object water = Reflection.getTypedMethod(fluidTypeFlowing, null, fluid, boolean.class).invoke(Reflection.getField(Reflection.getClass("{nms.world.level.material}.FluidTypes"), fluidTypeFlowing, 1).get(null), false);
|
||||||
|
private static final Iterable<?> registryBlockId = (Iterable<?>) Reflection.getField(TechHider.block, Reflection.getClass("{nms.core}.RegistryBlockID"), 0).get(null);
|
||||||
|
private static final Reflection.MethodInvoker getFluid = Reflection.getTypedMethod(TechHider.iBlockData, null, fluid);
|
||||||
|
@Override
|
||||||
|
public Set<Integer> materialToAllIds(Material material) {
|
||||||
|
Set<Integer> ids = new HashSet<>();
|
||||||
|
for(Object data : (ImmutableList<?>) getStateList.invoke(getStates.invoke(getBlock(material)))) {
|
||||||
|
ids.add(getCombinedId(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(material == Material.WATER) {
|
||||||
|
for(Object data : registryBlockId) {
|
||||||
|
if(getFluid.invoke(data) == water) {
|
||||||
|
ids.add(getCombinedId(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getBlock = Reflection.getTypedMethod(TechHider.craftMagicNumbers, "getBlock", TechHider.block, Material.class);
|
||||||
|
private Object getBlock(Material material) {
|
||||||
|
return getBlock.invoke(null, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getCombinedId = Reflection.getTypedMethod(TechHider.block, null, int.class, TechHider.iBlockData);
|
||||||
|
private int getCombinedId(Object blockData) {
|
||||||
|
return (int) getCombinedId.invoke(null, blockData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ChunkHider14 extends ChunkHider9 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, boolean skip, boolean blockPrecise) {
|
||||||
|
out.writeShort(in.readShort()); // Block count
|
||||||
|
byte bitsPerBlock = in.readByte();
|
||||||
|
out.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
if(bitsPerBlock < 9) {
|
||||||
|
obfuscationTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
|
||||||
|
obfuscate = Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
processDataArray(locationEvaluator, obfuscationTarget, obfuscate, in, out, bitsPerBlock, skip || (!blockPrecise && bitsPerBlock < 9));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
public class ProtocolWrapper14 extends ProtocolWrapper8 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
|
UnaryOperator<Object> blockBreakCloner = ProtocolUtils.shallowCloneGenerator(blockBreakPacket);
|
||||||
|
Reflection.FieldAccessor<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0);
|
||||||
|
Reflection.FieldAccessor<?> blockBreakBlockData = Reflection.getField(blockBreakPacket, TechHider.iBlockData, 0);
|
||||||
|
|
||||||
|
return (p, packet) -> {
|
||||||
|
switch (locationEvaluator.checkBlockPos(p, blockBreakPosition.get(packet))) {
|
||||||
|
case SKIP:
|
||||||
|
return packet;
|
||||||
|
case CHECK:
|
||||||
|
if(!TechHider.iBlockDataHidden(obfuscate, blockBreakBlockData.get(packet)))
|
||||||
|
return packet;
|
||||||
|
default:
|
||||||
|
packet = blockBreakCloner.apply(packet);
|
||||||
|
blockBreakBlockData.set(packet, obfuscationTarget);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.15")
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_14"))
|
||||||
|
compileOnly(project(":CommonCore"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:fastasyncworldedit:1.18")
|
||||||
|
compileOnly("de.steamwar:spigot:1.18")
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
|
||||||
|
compileOnly("com.mojang:datafixerupper:4.0.26")
|
||||||
|
compileOnly("io.netty:netty-all:4.1.68.Final")
|
||||||
|
compileOnly("com.mojang:authlib:1.5.25")
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
|
import net.minecraft.world.level.World;
|
||||||
|
import net.minecraft.world.level.chunk.Chunk;
|
||||||
|
import net.minecraft.world.level.lighting.LightEngine;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class CraftbukkitWrapper18 implements CraftbukkitWrapper.ICraftbukkitWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftChunk", "getHandle");
|
||||||
|
private static final Reflection.MethodInvoker getLightEngine = Reflection.getTypedMethod(World.class, null, LightEngine.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendChunk(Player p, int chunkX, int chunkZ) {
|
||||||
|
Chunk chunk = (Chunk) getHandle.invoke(p.getWorld().getChunkAt(chunkX, chunkZ));
|
||||||
|
TinyProtocol.instance.sendPacket(p, new ClientboundLevelChunkWithLightPacket(chunk, (LightEngine) getLightEngine.invoke(chunk.q), null, null, false, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProtocolWrapper18 implements ProtocolWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<List> equipmentStack = Reflection.getField(equipmentPacket, List.class, 0);
|
||||||
|
@Override
|
||||||
|
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
|
||||||
|
equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo");
|
||||||
|
private static final Class<?> playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
|
||||||
|
private static final Reflection.FieldAccessor<?> playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0);
|
||||||
|
private static final Reflection.FieldAccessor<List> playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0);
|
||||||
|
private static final EnumMap<PlayerInfoAction, Object> actions = new EnumMap<>(PlayerInfoAction.class);
|
||||||
|
static {
|
||||||
|
Object[] nativeActions = playerInfoActionClass.getEnumConstants();
|
||||||
|
actions.put(PlayerInfoAction.ADD, nativeActions[0]);
|
||||||
|
actions.put(PlayerInfoAction.GAMEMODE, nativeActions[1]);
|
||||||
|
actions.put(PlayerInfoAction.REMOVE, nativeActions[4]);
|
||||||
|
}
|
||||||
|
private static final Class<?> iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent");
|
||||||
|
private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData", GameProfile.class, int.class, enumGamemode, iChatBaseComponent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
|
||||||
|
Object packet = Reflection.newInstance(playerInfoPacket);
|
||||||
|
playerInfoAction.set(packet, actions.get(action));
|
||||||
|
playerInfoData.set(packet, Collections.singletonList(playerInfoDataConstructor.invoke(profile, 0, ProtocolWrapper.getGameModeById.invoke(null, mode.getValue()), null)));
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.sk89q.jnbt.NBTInputStream;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader;
|
||||||
|
import de.steamwar.sql.NoClipboardException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class WorldEditWrapper18 extends WorldEditWrapper14 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||||
|
//Use FAWE reader due to FAWE capability of reading corrupt FAWE schems
|
||||||
|
NBTInputStream nbtStream = new NBTInputStream(is);
|
||||||
|
try {
|
||||||
|
return (schemFormat ? new SpongeSchematicReader(nbtStream) : new MCEditSchematicReader(nbtStream)).read();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new NoClipboardException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.minecraft.core.IRegistry;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
|
import net.minecraft.resources.MinecraftKey;
|
||||||
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
|
import net.minecraft.world.level.block.entity.TileEntityTypes;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ChunkHider18 implements ChunkHider {
|
||||||
|
@Override
|
||||||
|
public Class<?> mapChunkPacket() {
|
||||||
|
return ClientboundLevelChunkWithLightPacket.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
|
||||||
|
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
|
||||||
|
private static final Reflection.FieldAccessor<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
|
return (p, packet) -> {
|
||||||
|
int chunkX = chunkXField.get(packet);
|
||||||
|
int chunkZ = chunkZField.get(packet);
|
||||||
|
if (locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
packet = chunkPacketCloner.apply(packet);
|
||||||
|
Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet));
|
||||||
|
|
||||||
|
tileEntities.set(dataWrapper, ((List<?>)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper));
|
||||||
|
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
|
||||||
|
int xOffset = 16*chunkX;
|
||||||
|
int zOffset = 16*chunkZ;
|
||||||
|
for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
|
||||||
|
int finalYOffset = yOffset;
|
||||||
|
int chunkY = yOffset/16;
|
||||||
|
dataHider((x, y, z) -> locationEvaluator.check(p, x+xOffset, y+finalYOffset, z+zOffset), obfuscationTarget, obfuscate, in, out, locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ), locationEvaluator.blockPrecise(p, chunkX, chunkY, chunkZ));
|
||||||
|
}
|
||||||
|
out.writeBytes(in); // MC appends a 0 byte at the end if there is a full chunk, idk why
|
||||||
|
|
||||||
|
byte[] data = new byte[out.readableBytes()];
|
||||||
|
out.readBytes(data);
|
||||||
|
dataField.set(dataWrapper, data);
|
||||||
|
|
||||||
|
chunkData.set(packet, dataWrapper);
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a");
|
||||||
|
protected static final Reflection.FieldAccessor<TileEntityTypes> entityType = Reflection.getField(tileEntity, TileEntityTypes.class, 0);
|
||||||
|
private static final IRegistry<?> tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null);
|
||||||
|
private static final Reflection.MethodInvoker getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
|
||||||
|
private static final Reflection.MethodInvoker getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
|
||||||
|
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
|
||||||
|
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, boolean skip, boolean blockPrecise) {
|
||||||
|
out.writeShort(in.readShort()); // Block count
|
||||||
|
|
||||||
|
containerWalker(in, out, 15, obfuscationTarget, skip ? Collections.emptySet() : obfuscate, (paletteTarget, dataArrayLength, bitsPerBlock) -> {
|
||||||
|
Set<Integer> palettedObfuscate;
|
||||||
|
if(bitsPerBlock < 15) {
|
||||||
|
palettedObfuscate = Collections.emptySet();
|
||||||
|
} else {
|
||||||
|
paletteTarget = obfuscationTarget;
|
||||||
|
palettedObfuscate = obfuscate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skip || dataArrayLength == 0 || (!blockPrecise && bitsPerBlock < 15)) {
|
||||||
|
out.writeBytes(in, dataArrayLength*8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long[] array = new long[dataArrayLength];
|
||||||
|
for(int i = 0; i < dataArrayLength; i++)
|
||||||
|
array[i] = in.readLong();
|
||||||
|
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096, array);
|
||||||
|
|
||||||
|
for (int y = 0; y < 16; y++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
int pos = (((y * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
switch (locationEvaluator.test(x, y, z)) {
|
||||||
|
case SKIP:
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
if(!palettedObfuscate.contains(values.a(pos)))
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
values.b(pos, paletteTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long l : values.a())
|
||||||
|
out.writeLong(l);
|
||||||
|
});
|
||||||
|
containerWalker(in, out, 6, 0, Collections.emptySet(), (paletteTarget, dataArrayLength, bitsPerBlock) -> out.writeBytes(in, dataArrayLength * 8)); // Biomes
|
||||||
|
}
|
||||||
|
|
||||||
|
private void containerWalker(ByteBuf in, ByteBuf out, int globalPalette, int obfuscationTarget, Set<Integer> obfuscate, TriConsumer<Integer, Integer, Byte> dataArray) {
|
||||||
|
byte bitsPerBlock = in.readByte();
|
||||||
|
out.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
//blockId -> obfuscate.contains(blockId) ? obfuscationTarget : blockId
|
||||||
|
if(bitsPerBlock == 0) {
|
||||||
|
int value = ProtocolUtils.readVarInt(in);
|
||||||
|
ProtocolUtils.writeVarInt(out, obfuscate.contains(value) ? obfuscationTarget : value);
|
||||||
|
}else if(bitsPerBlock < globalPalette) {
|
||||||
|
obfuscationTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataArrayLength = ProtocolUtils.readVarInt(in);
|
||||||
|
ProtocolUtils.writeVarInt(out, dataArrayLength);
|
||||||
|
dataArray.accept(obfuscationTarget, dataArrayLength, bitsPerBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface TriConsumer<X, Y, Z> {
|
||||||
|
void accept(X x, Y y, Z z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.core.SectionPosition;
|
||||||
|
import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak;
|
||||||
|
import net.minecraft.world.level.block.entity.TileEntitySign;
|
||||||
|
import net.minecraft.world.level.block.entity.TileEntityTypes;
|
||||||
|
import net.minecraft.world.level.block.state.IBlockData;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ProtocolWrapper18 implements ProtocolWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<SectionPosition> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<IBlockData[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0);
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
|
return (p, packet) -> {
|
||||||
|
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||||
|
int chunkX = TechHider.blockPositionX.get(chunkCoords);
|
||||||
|
int chunkY = TechHider.blockPositionY.get(chunkCoords);
|
||||||
|
int chunkZ = TechHider.blockPositionZ.get(chunkCoords);
|
||||||
|
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||||
|
final short[] oldPos = multiBlockChangePos.get(packet);
|
||||||
|
final IBlockData[] oldBlocks = multiBlockChangeBlocks.get(packet);
|
||||||
|
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
|
||||||
|
ArrayList<IBlockData> blocks = new ArrayList<>(oldPos.length);
|
||||||
|
for(int i = 0; i < oldPos.length; i++) {
|
||||||
|
short pos = oldPos[i];
|
||||||
|
IBlockData block = oldBlocks[i];
|
||||||
|
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 8 & 0xF), 16*chunkY + (pos & 0xF), 16*chunkZ + (pos >> 4 & 0xF))) {
|
||||||
|
case SKIP:
|
||||||
|
poss.add(pos);
|
||||||
|
blocks.add(block);
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
poss.add(pos);
|
||||||
|
blocks.add(TechHider.iBlockDataHidden(obfuscate, block) ? (IBlockData) obfuscationTarget : block);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blocks.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
short[] newPos = new short[poss.size()];
|
||||||
|
for(int i = 0; i < newPos.length; i++)
|
||||||
|
newPos[i] = poss.get(i);
|
||||||
|
|
||||||
|
multiBlockChangePos.set(packet, newPos);
|
||||||
|
multiBlockChangeBlocks.set(packet, blocks.toArray(new IBlockData[0]));
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<TileEntityTypes> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, TileEntityTypes.class, 0);
|
||||||
|
private static final TileEntityTypes<?> signType = Reflection.getField(TileEntityTypes.class, TileEntityTypes.class, 0, TileEntitySign.class).get(null);
|
||||||
|
@Override
|
||||||
|
public boolean unfilteredTileEntityDataAction(Object packet) {
|
||||||
|
return tileEntityType.get(packet) != signType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
|
return (p, packet) -> {
|
||||||
|
PacketPlayOutBlockBreak breakPacket = (PacketPlayOutBlockBreak) packet;
|
||||||
|
switch (locationEvaluator.checkBlockPos(p, breakPacket.b())) {
|
||||||
|
case SKIP:
|
||||||
|
return packet;
|
||||||
|
case CHECK:
|
||||||
|
if(!TechHider.iBlockDataHidden(obfuscate, breakPacket.c()))
|
||||||
|
return packet;
|
||||||
|
default:
|
||||||
|
return new PacketPlayOutBlockBreak(breakPacket.b(), (IBlockData) obfuscationTarget, breakPacket.d(), breakPacket.a());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_14"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_18"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:worldedit:1.15")
|
||||||
|
compileOnly("de.steamwar:spigot:1.19")
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
|
||||||
|
compileOnly("com.mojang:brigadier:1.0.18")
|
||||||
|
compileOnly("com.mojang:datafixerupper:4.0.26")
|
||||||
|
compileOnly("com.mojang:authlib:1.5.25")
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import net.minecraft.network.chat.IChatMutableComponent;
|
||||||
|
import net.minecraft.network.chat.contents.LiteralContents;
|
||||||
|
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
|
||||||
|
import net.minecraft.network.syncher.DataWatcher;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ChatWrapper19 implements ChatWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object stringToChatComponent(String text) {
|
||||||
|
return IChatMutableComponent.a(new LiteralContents(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues) {
|
||||||
|
ArrayList<DataWatcher.b<?>> nativeWatchers = new ArrayList<>(1);
|
||||||
|
for(int i = 0; i < dataWatcherKeyValues.length; i+=2) {
|
||||||
|
nativeWatchers.add(((DataWatcher.Item<?>) BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i+1])).e());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PacketPlayOutEntityMetadata(entityId, nativeWatchers);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import net.minecraft.SystemUtils;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||||
|
import net.minecraft.world.level.EnumGamemode;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
public class ProtocolWrapper19 implements ProtocolWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<List> equipmentStack = Reflection.getField(equipmentPacket, List.class, 0);
|
||||||
|
@Override
|
||||||
|
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
|
||||||
|
equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.ConstructorInvoker removePacketConstructor = Reflection.getConstructor(ClientboundPlayerInfoRemovePacket.class, List.class);
|
||||||
|
private static final Reflection.FieldAccessor<EnumSet> updateActions = Reflection.getField(ClientboundPlayerInfoUpdatePacket.class, EnumSet.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<List> updatePlayers = Reflection.getField(ClientboundPlayerInfoUpdatePacket.class, List.class, 0);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
|
||||||
|
if(action == PlayerInfoAction.REMOVE)
|
||||||
|
return removePacketConstructor.invoke(Collections.singletonList(profile.getId()));
|
||||||
|
|
||||||
|
Object packet = Reflection.newInstance(ClientboundPlayerInfoUpdatePacket.class);
|
||||||
|
updateActions.set(packet, action == PlayerInfoAction.ADD ? EnumSet.of(ClientboundPlayerInfoUpdatePacket.a.a, ClientboundPlayerInfoUpdatePacket.a.c) : EnumSet.of(ClientboundPlayerInfoUpdatePacket.a.c));
|
||||||
|
updatePlayers.set(packet, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.b(profile.getId(), profile, false, 0, EnumGamemode.a(mode.getValue()), null, null)));
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initTPSWarp(LongSupplier longSupplier) {
|
||||||
|
SystemUtils.a = () -> System.nanoTime() + longSupplier.getAsLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ProtocolWrapper19 extends ProtocolWrapper18 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.20")
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":CommonCore"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.8")
|
||||||
|
compileOnly("de.steamwar:worldedit:1.12")
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.minecraft.server.v1_8_R3.ChatComponentText;
|
||||||
|
import net.minecraft.server.v1_8_R3.MathHelper;
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
|
||||||
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playPling(Player player) {
|
||||||
|
player.playSound(player.getLocation(), Sound.ORB_PICKUP, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
|
||||||
|
((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(BaseComponent.toLegacyText(msg)), (byte)type.ordinal()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherObject(int index, Class<?> type) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> watchableObject = Reflection.getClass("{nms}.DataWatcher$WatchableObject");
|
||||||
|
private static final Reflection.ConstructorInvoker watchableObjectConstructor = Reflection.getConstructor(watchableObject, int.class, int.class, Object.class);
|
||||||
|
private static final Map<Class<?>, Integer> watchableDatatypes = new HashMap<>();
|
||||||
|
static {
|
||||||
|
watchableDatatypes.put(byte.class, 0);
|
||||||
|
watchableDatatypes.put(short.class, 1);
|
||||||
|
watchableDatatypes.put(int.class, 2);
|
||||||
|
watchableDatatypes.put(float.class, 3);
|
||||||
|
watchableDatatypes.put(String.class, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherItem(Object dwo, Object value) {
|
||||||
|
return watchableObjectConstructor.invoke(watchableDatatypes.get(value.getClass()), dwo, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
|
||||||
|
Reflection.FieldAccessor<Integer> posX = Reflection.getField(packetClass, int.class, fieldOffset);
|
||||||
|
Reflection.FieldAccessor<Integer> posY = Reflection.getField(packetClass, int.class, fieldOffset +1);
|
||||||
|
Reflection.FieldAccessor<Integer> posZ = Reflection.getField(packetClass, int.class, fieldOffset +2);
|
||||||
|
|
||||||
|
return (packet, x, y, z) -> {
|
||||||
|
posX.set(packet, MathHelper.floor(x * 32));
|
||||||
|
posY.set(packet, MathHelper.floor(y * 32));
|
||||||
|
posZ.set(packet, MathHelper.floor(z * 32));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.PositionSetter getRelMoveSetter(Class<?> packetClass) {
|
||||||
|
Reflection.FieldAccessor<?> moveX = Reflection.getField(packetClass, "b", byte.class);
|
||||||
|
Reflection.FieldAccessor<?> moveY = Reflection.getField(packetClass, "c", byte.class);
|
||||||
|
Reflection.FieldAccessor<?> moveZ = Reflection.getField(packetClass, "d", byte.class);
|
||||||
|
|
||||||
|
return (packet, x, y, z) -> {
|
||||||
|
moveX.set(packet, (byte)(x*32));
|
||||||
|
moveY.set(packet, (byte)(y*32));
|
||||||
|
moveZ.set(packet, (byte)(z*32));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
|
||||||
|
return (packet, uuid) -> {};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChatWrapper8 implements ChatWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.ConstructorInvoker chatComponentConstructor = Reflection.getConstructor(Reflection.getClass("{nms.network.chat}.ChatComponentText"), String.class);
|
||||||
|
@Override
|
||||||
|
public Object stringToChatComponent(String text) {
|
||||||
|
return chatComponentConstructor.invoke(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> metadataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityMetadata");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> metadataEntity = Reflection.getField(metadataPacket, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<List> metadataMetadata = Reflection.getField(metadataPacket, List.class, 0);
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues) {
|
||||||
|
Object packet = Reflection.newInstance(metadataPacket);
|
||||||
|
metadataEntity.set(packet, entityId);
|
||||||
|
|
||||||
|
ArrayList<Object> nativeWatchers = new ArrayList<>(1);
|
||||||
|
for(int i = 0; i < dataWatcherKeyValues.length; i+=2) {
|
||||||
|
nativeWatchers.add(BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i+1]));
|
||||||
|
}
|
||||||
|
metadataMetadata.set(packet, nativeWatchers);
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R3.CraftChunk;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class CraftbukkitWrapper8 implements CraftbukkitWrapper.ICraftbukkitWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendChunk(Player p, int chunkX, int chunkZ) {
|
||||||
|
((CraftPlayer)p).getHandle().playerConnection.sendPacket(new PacketPlayOutMapChunk(((CraftChunk)p.getWorld().getChunkAt(chunkX, chunkZ)).getHandle(), true, 65535));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.SkullMeta;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<String> scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, String.class, 1);
|
||||||
|
private static final Class<?> scoreActionEnum = Reflection.getClass("{nms}.PacketPlayOutScoreboardScore$EnumScoreboardAction");
|
||||||
|
private static final Reflection.FieldAccessor<?> scoreAction = Reflection.getField(FlatteningWrapper.scoreboardScore, scoreActionEnum, 0);
|
||||||
|
private static final Object scoreActionChange = scoreActionEnum.getEnumConstants()[0];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setScoreboardTitle(Object packet, String title) {
|
||||||
|
scoreboardName.set(packet, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setScoreAction(Object packet) {
|
||||||
|
scoreAction.set(packet, scoreActionChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Material getMaterial(String material) {
|
||||||
|
try{
|
||||||
|
return Material.valueOf(material);
|
||||||
|
}catch(IllegalArgumentException e){
|
||||||
|
return Material.STONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Material getDye(int colorCode) {
|
||||||
|
return Material.INK_SACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack setSkullOwner(String player) {
|
||||||
|
ItemStack head = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
|
||||||
|
SkullMeta headmeta = (SkullMeta) head.getItemMeta();
|
||||||
|
headmeta.setOwner(player.startsWith(".") ? player.substring(1) : player);
|
||||||
|
headmeta.setDisplayName(player);
|
||||||
|
head.setItemMeta(headmeta);
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPose(FlatteningWrapper.EntityPose pose) {
|
||||||
|
return Byte.valueOf((byte)(pose == FlatteningWrapper.EntityPose.SNEAKING ? 2 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> dataWatcher = Reflection.getClass("{nms}.DataWatcher");
|
||||||
|
private static final Class<?> namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn");
|
||||||
|
private static final Reflection.FieldAccessor<?> namedSpawnDataWatcher = Reflection.getField(namedSpawnPacket, dataWatcher, 0);
|
||||||
|
private static final Class<?> entity = Reflection.getClass("{nms}.Entity");
|
||||||
|
private static final Reflection.ConstructorInvoker dataWatcherConstructor = Reflection.getConstructor(dataWatcher, entity);
|
||||||
|
@Override
|
||||||
|
public void setNamedSpawnPacketDataWatcher(Object packet) {
|
||||||
|
namedSpawnDataWatcher.set(packet, dataWatcherConstructor.invoke((Object) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object formatDisplayName(String displayName) {
|
||||||
|
return displayName != null ? displayName : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<?> spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
|
||||||
|
private static final Map<EntityType, Object> types = new HashMap<>();
|
||||||
|
static {
|
||||||
|
types.put(EntityType.PRIMED_TNT, 50);
|
||||||
|
types.put(EntityType.ARMOR_STAND, 30);
|
||||||
|
types.put(EntityType.ARROW, 60);
|
||||||
|
types.put(EntityType.FIREBALL, 63);
|
||||||
|
types.put(EntityType.ITEM_FRAME, 18);
|
||||||
|
types.put(EntityType.FALLING_BLOCK, 21);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setSpawnPacketType(Object packet, EntityType type) {
|
||||||
|
(type.isAlive() ? spawnLivingType : spawnType).set(packet, types.get(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getViewDistance(Player player) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.MethodInvoker save = Reflection.getMethod("{obc}.CraftWorld", "save", boolean.class);
|
||||||
|
@Override
|
||||||
|
public void syncSave(World world) {
|
||||||
|
save.invoke(world, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class IDConverter8 {
|
||||||
|
|
||||||
|
private final Map<String, Set<String>> availibleAttributes;
|
||||||
|
private final Map<String, Map<Set<String>, BlockTypeID>> map;
|
||||||
|
|
||||||
|
public IDConverter8() {
|
||||||
|
Map<String, Map<Set<String>, BlockTypeID>> map = new HashMap<>();
|
||||||
|
|
||||||
|
YamlConfiguration legacy = YamlConfiguration.loadConfiguration(new InputStreamReader(Objects.requireNonNull(IDConverter8.class.getClassLoader().getResourceAsStream("legacy.yml"))));
|
||||||
|
for(String blockString : legacy.getKeys(false)){
|
||||||
|
String[] legacyBlockId = legacy.getString(blockString).split(":");
|
||||||
|
|
||||||
|
map.computeIfAbsent(getBlockId(blockString), bId -> {
|
||||||
|
Map<Set<String>, BlockTypeID> attributeMap = new HashMap<>();
|
||||||
|
attributeMap.put(new HashSet<>(), new BlockTypeID(legacyBlockId[0], "0"));
|
||||||
|
return attributeMap;
|
||||||
|
}).put(getAttributes(blockString), new BlockTypeID(legacyBlockId[0], legacyBlockId[1]));
|
||||||
|
}
|
||||||
|
this.map = map;
|
||||||
|
|
||||||
|
Map<String, Set<String>> availableAttributes = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Map<Set<String>, BlockTypeID>> entry : map.entrySet()) {
|
||||||
|
availableAttributes.put(entry.getKey(), entry.getValue().keySet().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
this.availibleAttributes = availableAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockTypeID getId(String blockString) {
|
||||||
|
String blockId = getBlockId(blockString);
|
||||||
|
Map<Set<String>, IDConverter8.BlockTypeID> attributeMap = map.get(blockId);
|
||||||
|
if(attributeMap == null) { // Block nonexistent pre-flattening
|
||||||
|
return new BlockTypeID("0", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> attributes = getAttributes(blockString);
|
||||||
|
Set<String> knownAttributes = this.availibleAttributes.get(blockId);
|
||||||
|
attributes.removeIf(attribute -> !knownAttributes.contains(attribute));
|
||||||
|
|
||||||
|
long bestMatch = -1;
|
||||||
|
BlockTypeID blockID = null;
|
||||||
|
for(Map.Entry<Set<String>, BlockTypeID> entry : attributeMap.entrySet()) {
|
||||||
|
Set<String> attrs = entry.getKey();
|
||||||
|
if(attrs.size() <= bestMatch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
long matching = attributes.stream().filter(attrs::contains).count();
|
||||||
|
if(matching > bestMatch) {
|
||||||
|
blockID = entry.getValue();
|
||||||
|
bestMatch = matching;
|
||||||
|
|
||||||
|
if(bestMatch == attributes.size())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blockID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBlockId(String blockString) {
|
||||||
|
return blockString.split("\\[", 2)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getAttributes(String blockString) {
|
||||||
|
Set<String> attributes = new HashSet<>();
|
||||||
|
if(blockString.contains("["))
|
||||||
|
Collections.addAll(attributes, blockString.split("\\[")[1].replace("]", "").split(","));
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BlockTypeID{
|
||||||
|
private final int blockId;
|
||||||
|
private final byte dataId;
|
||||||
|
|
||||||
|
private BlockTypeID(String blockId, String dataId) {
|
||||||
|
this.blockId = Integer.parseInt(blockId);
|
||||||
|
this.dataId = Byte.parseByte(dataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBlockId() {
|
||||||
|
return blockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte getDataId() {
|
||||||
|
return dataId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
public class LocaleChangeWrapper8 implements LocaleChangeWrapper {
|
||||||
|
// Event not available in 1.8-1.10
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProtocolWrapper8 implements ProtocolWrapper {
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<?> equipmentSlot;
|
||||||
|
static {
|
||||||
|
if(Core.getVersion() == 8) {
|
||||||
|
equipmentSlot = Reflection.getField(equipmentPacket, int.class, 1);
|
||||||
|
} else {
|
||||||
|
Class<?> enumItemSlot = Reflection.getClass("{nms.world.entity}.EnumItemSlot");
|
||||||
|
equipmentSlot = Reflection.getField(equipmentPacket, enumItemSlot, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<?> equipmentStack = Reflection.getField(equipmentPacket, itemStack, 0);
|
||||||
|
@Override
|
||||||
|
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
|
||||||
|
equipmentSlot.set(packet, slot);
|
||||||
|
equipmentStack.set(packet, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo");
|
||||||
|
private static final Class<?> playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
|
||||||
|
private static final Reflection.FieldAccessor<?> playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0);
|
||||||
|
private static final Reflection.FieldAccessor<List> playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0);
|
||||||
|
private static final EnumMap<PlayerInfoAction, Object> actions = new EnumMap<>(PlayerInfoAction.class);
|
||||||
|
static {
|
||||||
|
Object[] nativeActions = playerInfoActionClass.getEnumConstants();
|
||||||
|
actions.put(PlayerInfoAction.ADD, nativeActions[0]);
|
||||||
|
actions.put(PlayerInfoAction.GAMEMODE, nativeActions[1]);
|
||||||
|
actions.put(PlayerInfoAction.REMOVE, nativeActions[4]);
|
||||||
|
}
|
||||||
|
private static final Class<?> iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent");
|
||||||
|
private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData", playerInfoPacket, GameProfile.class, int.class, enumGamemode, iChatBaseComponent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
|
||||||
|
Object packet = Reflection.newInstance(playerInfoPacket);
|
||||||
|
playerInfoAction.set(packet, actions.get(action));
|
||||||
|
playerInfoData.set(packet, Collections.singletonList(playerInfoDataConstructor.invoke(packet, profile, 0, ProtocolWrapper.getGameModeById.invoke(null, mode.getValue()), null)));
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
public class RecipeDiscoverWrapper8 implements RecipeDiscoverWrapper {
|
||||||
|
// Event not available pre flattening
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.sk89q.jnbt.*;
|
||||||
|
import com.sk89q.worldedit.*;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||||
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
|
||||||
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
|
import com.sk89q.worldedit.world.registry.WorldData;
|
||||||
|
import de.steamwar.sql.NoClipboardException;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class WorldEditWrapper8 implements WorldEditWrapper.IWorldEditWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
|
||||||
|
ClipboardHolder clipboardHolder;
|
||||||
|
try {
|
||||||
|
clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard();
|
||||||
|
} catch (EmptyClipboardException e) {
|
||||||
|
throw new NoClipboardException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Clipboard clipboard = clipboardHolder.getClipboard();
|
||||||
|
if(clipboard == null)
|
||||||
|
throw new NoClipboardException();
|
||||||
|
|
||||||
|
PipedOutputStream outputStream = new PipedOutputStream();
|
||||||
|
PipedInputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = new PipedInputStream(outputStream, 4096);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Could not init piped input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Core.getInstance().getLogger().log(Level.SEVERE, "Could not write schematic", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Core.getInstance().getLogger().log(Level.SEVERE, "Could not close schem writer", e);
|
||||||
|
}
|
||||||
|
}, "SchemWriter").start();
|
||||||
|
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) {
|
||||||
|
WorldData world = new BukkitWorld(player.getWorld()).getWorldData();
|
||||||
|
Clipboard clipboard;
|
||||||
|
try {
|
||||||
|
clipboard = getClipboard(is, schemFormat);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
|
||||||
|
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard, world));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||||
|
if(schemFormat)
|
||||||
|
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||||
|
else
|
||||||
|
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SpongeSchematicReader implements ClipboardReader {
|
||||||
|
|
||||||
|
private final NBTInputStream inputStream;
|
||||||
|
private int schematicVersion = -1;
|
||||||
|
|
||||||
|
SpongeSchematicReader(NBTInputStream inputStream) {
|
||||||
|
Preconditions.checkNotNull(inputStream);
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clipboard read(WorldData worldData) throws IOException {
|
||||||
|
CompoundTag schematicTag = this.getBaseTag();
|
||||||
|
if (this.schematicVersion == 1) {
|
||||||
|
return this.readSchematic(schematicTag);
|
||||||
|
} else if (this.schematicVersion == 2) {
|
||||||
|
return this.readSchematic(schematicTag);
|
||||||
|
} else {
|
||||||
|
throw new IOException("This schematic version is currently not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompoundTag getBaseTag() throws IOException {
|
||||||
|
NamedTag rootTag = this.inputStream.readNamedTag();
|
||||||
|
if (!rootTag.getName().equals("Schematic")) {
|
||||||
|
throw new IOException("Tag 'Schematic' does not exist or is not first");
|
||||||
|
} else {
|
||||||
|
CompoundTag schematicTag = (CompoundTag)rootTag.getTag();
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
this.schematicVersion = (requireTag(schematic, "Version", IntTag.class)).getValue();
|
||||||
|
return schematicTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockArrayClipboard readSchematic(CompoundTag schematicTag) throws IOException {
|
||||||
|
IDConverter8 ids = new IDConverter8();
|
||||||
|
|
||||||
|
Map<String, Tag> schematic = schematicTag.getValue();
|
||||||
|
int width = (requireTag(schematic, "Width", ShortTag.class)).getValue();
|
||||||
|
int height = (requireTag(schematic, "Height", ShortTag.class)).getValue();
|
||||||
|
int length = (requireTag(schematic, "Length", ShortTag.class)).getValue();
|
||||||
|
IntArrayTag offsetTag = getTag(schematic, "Offset", IntArrayTag.class);
|
||||||
|
int[] offsetParts;
|
||||||
|
if (offsetTag != null) {
|
||||||
|
offsetParts = offsetTag.getValue();
|
||||||
|
if (offsetParts.length != 3)
|
||||||
|
throw new IOException("Invalid offset specified in schematic.");
|
||||||
|
} else {
|
||||||
|
offsetParts = new int[]{0, 0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockVector min = new BlockVector(offsetParts[0], offsetParts[1], offsetParts[2]);
|
||||||
|
CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class);
|
||||||
|
Vector origin;
|
||||||
|
CuboidRegion region;
|
||||||
|
if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) {
|
||||||
|
Map<String, Tag> metadata = metadataTag.getValue();
|
||||||
|
int offsetX = (requireTag(metadata, "WEOffsetX", IntTag.class)).getValue();
|
||||||
|
int offsetY = (requireTag(metadata, "WEOffsetY", IntTag.class)).getValue();
|
||||||
|
int offsetZ = (requireTag(metadata, "WEOffsetZ", IntTag.class)).getValue();
|
||||||
|
BlockVector offset = new BlockVector(offsetX, offsetY, offsetZ);
|
||||||
|
origin = min.subtract(offset);
|
||||||
|
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
|
||||||
|
} else {
|
||||||
|
origin = min;
|
||||||
|
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class);
|
||||||
|
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
|
||||||
|
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue())
|
||||||
|
throw new IOException("Block palette size does not match expected size.");
|
||||||
|
|
||||||
|
Map<Integer, BaseBlock> palette = new HashMap<>();
|
||||||
|
ParserContext parserContext = new ParserContext();
|
||||||
|
parserContext.setRestricted(false);
|
||||||
|
parserContext.setPreferringWildcard(false);
|
||||||
|
|
||||||
|
for(String palettePart : paletteObject.keySet()) {
|
||||||
|
IDConverter8.BlockTypeID blockID = ids.getId(palettePart);
|
||||||
|
palette.put(requireTag(paletteObject, palettePart, IntTag.class).getValue(), new BaseBlock(blockID.getBlockId(), blockID.getDataId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue();
|
||||||
|
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
|
||||||
|
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class);
|
||||||
|
if (tileEntities == null) {
|
||||||
|
tileEntities = getTag(schematic, "TileEntities", ListTag.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tileEntities != null) {
|
||||||
|
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream().map((tag) ->
|
||||||
|
(CompoundTag)tag
|
||||||
|
).map(CompoundTag::getValue).collect(Collectors.toList());
|
||||||
|
|
||||||
|
BlockVector pt;
|
||||||
|
Map<String, Tag> tileEntity;
|
||||||
|
for(Iterator<Map<String, Tag>> var20 = tileEntityTags.iterator(); var20.hasNext(); tileEntitiesMap.put(pt, tileEntity)) {
|
||||||
|
tileEntity = var20.next();
|
||||||
|
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
|
||||||
|
pt = new BlockVector(pos[0], pos[1], pos[2]);
|
||||||
|
Map<String, Tag> values = Maps.newHashMap(tileEntity);
|
||||||
|
values.put("x", new IntTag(pt.getBlockX()));
|
||||||
|
values.put("y", new IntTag(pt.getBlockY()));
|
||||||
|
values.put("z", new IntTag(pt.getBlockZ()));
|
||||||
|
values.put("id", values.get("Id"));
|
||||||
|
values.remove("Id");
|
||||||
|
values.remove("Pos");
|
||||||
|
tileEntity = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
|
||||||
|
clipboard.setOrigin(origin);
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < blocks.length; ++index) {
|
||||||
|
int value = 0;
|
||||||
|
int varintLength = 0;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
value |= (blocks[i] & 127) << varintLength++ * 7;
|
||||||
|
if (varintLength > 5) {
|
||||||
|
throw new IOException("VarInt too big (probably corrupted data)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((blocks[i] & 128) != 128) {
|
||||||
|
++i;
|
||||||
|
int y = index / (width * length);
|
||||||
|
int z = index % (width * length) / width;
|
||||||
|
int x = index % (width * length) % width;
|
||||||
|
BaseBlock block = palette.get(value);
|
||||||
|
BlockVector pt = new BlockVector(x, y, z);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (tileEntitiesMap.containsKey(pt)) {
|
||||||
|
block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt)));
|
||||||
|
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block);
|
||||||
|
} else {
|
||||||
|
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch (WorldEditException var30) {
|
||||||
|
throw new IOException("Failed to load a block in the schematic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clipboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
|
||||||
|
if (!items.containsKey(key)) {
|
||||||
|
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
|
||||||
|
} else {
|
||||||
|
Tag tag = items.get(key);
|
||||||
|
if (!expected.isInstance(tag)) {
|
||||||
|
throw new IOException(key + " tag is not of tag type " + expected.getName());
|
||||||
|
} else {
|
||||||
|
return expected.cast(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Tag> T getTag(Map<String, Tag> items, String key, Class<T> expected) {
|
||||||
|
if (!items.containsKey(key)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Tag test = items.get(key);
|
||||||
|
return !expected.isInstance(test) ? null : expected.cast(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BlockIds8 implements BlockIds {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public int materialToId(Material material) {
|
||||||
|
return material.getId() << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> materialToAllIds(Material material) {
|
||||||
|
return Collections.singleton(materialToId(material));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ChunkHider8 implements ChunkHider {
|
||||||
|
|
||||||
|
protected static final Class<?> mapChunkPacket = Reflection.getClass("{nms}.PacketPlayOutMapChunk");
|
||||||
|
@Override
|
||||||
|
public Class<?> mapChunkPacket() {
|
||||||
|
return mapChunkPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
|
return (player, packet) -> packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ProtocolWrapper8 implements ProtocolWrapper {
|
||||||
|
private static final Class<?> chunkCoordinateIntPair = Reflection.getClass("{nms}.ChunkCoordIntPair");
|
||||||
|
private static final Reflection.FieldAccessor<?> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, chunkCoordinateIntPair, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkCoordinateX = Reflection.getField(chunkCoordinateIntPair, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkCoordinateZ = Reflection.getField(chunkCoordinateIntPair, int.class, 1);
|
||||||
|
private static final Class<?> multiBlockChangeInfo = Reflection.getClass("{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
|
||||||
|
private static final Reflection.ConstructorInvoker multiBlockChangeInfoConstructor = Reflection.getConstructor(multiBlockChangeInfo, TechHider.multiBlockChangePacket, short.class, TechHider.iBlockData);
|
||||||
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoBlock = Reflection.getField(multiBlockChangeInfo, TechHider.iBlockData, 0);
|
||||||
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoPos = Reflection.getField(multiBlockChangeInfo, short.class, 0);
|
||||||
|
private static final Class<?> multiBlockChangeInfoArray = Reflection.getClass("[L{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo;");
|
||||||
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfos = Reflection.getField(TechHider.multiBlockChangePacket, multiBlockChangeInfoArray, 0);
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
|
return (p, packet) -> {
|
||||||
|
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||||
|
int chunkX = chunkCoordinateX.get(chunkCoords);
|
||||||
|
int chunkZ = chunkCoordinateZ.get(chunkCoords);
|
||||||
|
if(locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||||
|
Object[] mbcis = (Object[]) multiBlockChangeInfos.get(packet);
|
||||||
|
ArrayList<Object> blockChangeInfos = new ArrayList<>(mbcis.length);
|
||||||
|
for(Object mbci : mbcis) {
|
||||||
|
short pos = (short) multiBlockChangeInfoPos.get(mbci);
|
||||||
|
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 12 & 0xF), pos & 0xFF, 16*chunkZ + (pos >> 8 & 0xF))) {
|
||||||
|
case SKIP:
|
||||||
|
blockChangeInfos.add(mbci);
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
blockChangeInfos.add(TechHider.iBlockDataHidden(obfuscate, multiBlockChangeInfoBlock.get(mbci)) ? multiBlockChangeInfoConstructor.invoke(packet, pos, obfuscationTarget) : mbci);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blockChangeInfos.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
multiBlockChangeInfos.set(packet, blockChangeInfos.toArray((Object[])Array.newInstance(multiBlockChangeInfo, 0)));
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<Integer> tileEntityDataAction = Reflection.getField(TechHider.tileEntityDataPacket, int.class, 0);
|
||||||
|
@Override
|
||||||
|
public boolean unfilteredTileEntityDataAction(Object packet) {
|
||||||
|
return tileEntityDataAction.get(packet) != 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator bypass) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_Main"))
|
||||||
|
compileOnly(project(":SpigotCore:SpigotCore_8"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:spigot:1.9")
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.viaversion.viaversion.api.Via;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class BountifulWrapper9 implements BountifulWrapper.IBountifulWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playPling(Player player) {
|
||||||
|
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
|
||||||
|
if(type == ChatMessageType.CHAT && Via.getAPI().getPlayerVersion(player.getUniqueId()) >= 759)
|
||||||
|
type = ChatMessageType.SYSTEM;
|
||||||
|
|
||||||
|
player.spigot().sendMessage(type, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> dataWatcherObject = Reflection.getClass("{nms.network.syncher}.DataWatcherObject");
|
||||||
|
private static final Class<?> dataWatcherRegistry = Reflection.getClass("{nms.network.syncher}.DataWatcherRegistry");
|
||||||
|
private static final Class<?> dataWatcherSerializer = Reflection.getClass("{nms.network.syncher}.DataWatcherSerializer");
|
||||||
|
private static final Reflection.ConstructorInvoker dataWatcherObjectConstructor = Reflection.getConstructor(dataWatcherObject, int.class, dataWatcherSerializer);
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherObject(int index, Class<?> type) {
|
||||||
|
return dataWatcherObjectConstructor.invoke(index, Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> item = Reflection.getClass("{nms.network.syncher}.DataWatcher$Item");
|
||||||
|
private static final Reflection.ConstructorInvoker itemConstructor = Reflection.getConstructor(item, dataWatcherObject, Object.class);
|
||||||
|
@Override
|
||||||
|
public Object getDataWatcherItem(Object dwo, Object value) {
|
||||||
|
return itemConstructor.invoke(dwo, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
|
||||||
|
Reflection.FieldAccessor<Double> posX = Reflection.getField(packetClass, double.class, fieldOffset);
|
||||||
|
Reflection.FieldAccessor<Double> posY = Reflection.getField(packetClass, double.class, fieldOffset+1);
|
||||||
|
Reflection.FieldAccessor<Double> posZ = Reflection.getField(packetClass, double.class, fieldOffset+2);
|
||||||
|
|
||||||
|
return (packet, x, y, z) -> {
|
||||||
|
posX.set(packet, x);
|
||||||
|
posY.set(packet, y);
|
||||||
|
posZ.set(packet, z);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.PositionSetter getRelMoveSetter(Class<?> packetClass) {
|
||||||
|
Class<?> type = Core.getVersion() > 12 ? short.class : int.class;
|
||||||
|
Reflection.FieldAccessor<?> moveX = Reflection.getField(packetClass, "b", type);
|
||||||
|
Reflection.FieldAccessor<?> moveY = Reflection.getField(packetClass, "c", type);
|
||||||
|
Reflection.FieldAccessor<?> moveZ = Reflection.getField(packetClass, "d", type);
|
||||||
|
|
||||||
|
return (packet, x, y, z) -> {
|
||||||
|
moveX.set(packet, (short)(x*4096));
|
||||||
|
moveY.set(packet, (short)(y*4096));
|
||||||
|
moveZ.set(packet, (short)(z*4096));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
|
||||||
|
Reflection.FieldAccessor<UUID> uuidField = Reflection.getField(packetClass, UUID.class, 0);
|
||||||
|
|
||||||
|
return uuidField::set;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class CraftbukkitWrapper9 implements CraftbukkitWrapper.ICraftbukkitWrapper {
|
||||||
|
|
||||||
|
private static final Class<?> chunk = Reflection.getClass("{nms.world.level.chunk}.Chunk");
|
||||||
|
private static final Class<?> packetPlayOutMapChunk = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutMapChunk");
|
||||||
|
private static final Reflection.ConstructorInvoker newPacketPlayOutMapChunk = Reflection.getConstructor(packetPlayOutMapChunk, chunk, int.class);
|
||||||
|
private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftChunk", "getHandle");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendChunk(Player p, int chunkX, int chunkZ) {
|
||||||
|
TinyProtocol.instance.sendPacket(p, newPacketPlayOutMapChunk.invoke(getHandle.invoke(p.getWorld().getChunkAt(chunkX, chunkZ)), 65535));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ChunkHider9 extends ChunkHider8 {
|
||||||
|
|
||||||
|
private static final UnaryOperator<Object> mapChunkCloner = ProtocolUtils.shallowCloneGenerator(mapChunkPacket);
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<Integer> mapChunkX = Reflection.getField(mapChunkPacket, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> mapChunkZ = Reflection.getField(mapChunkPacket, int.class, 1);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> mapChunkBitMask = Reflection.getField(mapChunkPacket, int.class, 2);
|
||||||
|
private static final Reflection.FieldAccessor<List> mapChunkBlockEntities = Reflection.getField(mapChunkPacket, List.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<byte[]> mapChunkData = Reflection.getField(mapChunkPacket, byte[].class, 0);
|
||||||
|
|
||||||
|
private static final Class<?> nbtTagCompound = Reflection.getClass("{nms.nbt}.NBTTagCompound");
|
||||||
|
private static final Reflection.MethodInvoker nbtTagGetString = Reflection.getTypedMethod(nbtTagCompound, null, String.class, String.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
|
return (p, packet) -> {
|
||||||
|
int chunkX = mapChunkX.get(packet);
|
||||||
|
int chunkZ = mapChunkZ.get(packet);
|
||||||
|
if (locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
packet = mapChunkCloner.apply(packet);
|
||||||
|
mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter(
|
||||||
|
nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id"))
|
||||||
|
).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
int xOffset = 16*chunkX;
|
||||||
|
int zOffset = 16*chunkZ;
|
||||||
|
int primaryBitMask = mapChunkBitMask.get(packet);
|
||||||
|
ByteBuf in = Unpooled.wrappedBuffer(mapChunkData.get(packet));
|
||||||
|
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
|
||||||
|
for(int chunkY = 0; chunkY < p.getWorld().getMaxHeight()/16; chunkY++) {
|
||||||
|
if(((1 << chunkY) & primaryBitMask) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int yOffset = 16*chunkY;
|
||||||
|
dataHider((x, y, z) -> locationEvaluator.check(p, x+xOffset, y+yOffset, z+zOffset), obfuscationTarget, obfuscate, in, out, locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ), locationEvaluator.blockPrecise(p, chunkX, chunkY, chunkZ));
|
||||||
|
}
|
||||||
|
byte[] data = new byte[out.readableBytes()];
|
||||||
|
out.readBytes(data);
|
||||||
|
mapChunkData.set(packet, data);
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, boolean skip, boolean blockPrecise) {
|
||||||
|
byte bitsPerBlock = in.readByte();
|
||||||
|
out.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
int paletteTarget = ChunkHider.processPalette(obfuscationTarget, skip ? Collections.emptySet() : obfuscate, in, out);
|
||||||
|
if(bitsPerBlock < 13) {
|
||||||
|
obfuscationTarget = paletteTarget;
|
||||||
|
obfuscate = Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
processDataArray(locationEvaluator, obfuscationTarget, obfuscate, in, out, bitsPerBlock, skip || (!blockPrecise && bitsPerBlock < 9));
|
||||||
|
|
||||||
|
out.writeBytes(in, 4096); //Skylight (Not in Nether/End!!!) 2048 + Blocklight 2048
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processDataArray(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, int bitsPerBlock, boolean skip) {
|
||||||
|
int dataArrayLength = ProtocolUtils.readVarInt(in);
|
||||||
|
ProtocolUtils.writeVarInt(out, dataArrayLength);
|
||||||
|
|
||||||
|
if(skip) {
|
||||||
|
out.writeBytes(in, dataArrayLength*8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableValueArray values = new VariableValueArray(bitsPerBlock, dataArrayLength, in);
|
||||||
|
for (int y = 0; y < 16; y++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
int pos = (((y * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
switch (locationEvaluator.test(x, y, z)) {
|
||||||
|
case SKIP:
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
if(!obfuscate.contains(values.get(pos)))
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
values.set(pos, obfuscationTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(long l : values.backing)
|
||||||
|
out.writeLong(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VariableValueArray {
|
||||||
|
private final long[] backing;
|
||||||
|
private final int bitsPerValue;
|
||||||
|
private final long valueMask;
|
||||||
|
|
||||||
|
public VariableValueArray(int bitsPerEntry, int dataArrayLength, ByteBuf in) {
|
||||||
|
this.bitsPerValue = bitsPerEntry;
|
||||||
|
this.valueMask = (1L << this.bitsPerValue) - 1;
|
||||||
|
|
||||||
|
this.backing = new long[dataArrayLength];
|
||||||
|
for(int i = 0; i < dataArrayLength; i++)
|
||||||
|
backing[i] = in.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int get(int index) {
|
||||||
|
index *= bitsPerValue;
|
||||||
|
int i0 = index >> 6;
|
||||||
|
int i1 = index & 0x3f;
|
||||||
|
|
||||||
|
long value = backing[i0] >>> i1;
|
||||||
|
|
||||||
|
// The value is divided over two long values
|
||||||
|
if (i1 + bitsPerValue > 64)
|
||||||
|
value |= backing[++i0] << 64 - i1;
|
||||||
|
|
||||||
|
return (int) (value & valueMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int index, int value) {
|
||||||
|
index *= bitsPerValue;
|
||||||
|
int i0 = index >> 6;
|
||||||
|
int i1 = index & 0x3f;
|
||||||
|
|
||||||
|
backing[i0] = backing[i0] & ~(valueMask << i1) | (value & valueMask) << i1;
|
||||||
|
int i2 = i1 + bitsPerValue;
|
||||||
|
// The value is divided over two long values
|
||||||
|
if (i2 > 64) {
|
||||||
|
i0++;
|
||||||
|
backing[i0] = backing[i0] & -(1L << i2 - 64) | value >> 64 - i1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("base")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.steamwar"
|
||||||
|
version = ""
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs("src/")
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs("src/")
|
||||||
|
exclude("**/*.java", "**/*.kt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.32")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.32")
|
||||||
|
|
||||||
|
compileOnly(project(":CommonCore"))
|
||||||
|
compileOnly(project(":CommandFramework"))
|
||||||
|
|
||||||
|
compileOnly("de.steamwar:worldedit:1.12")
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
|
||||||
|
compileOnly("io.netty:netty-all:4.1.68.Final")
|
||||||
|
compileOnly("com.mojang:authlib:1.5.25")
|
||||||
|
compileOnly("mysql:mysql-connector-java:5.1.49")
|
||||||
|
compileOnly("com.viaversion:viaversion-api:4.3.1")
|
||||||
|
compileOnly("it.unimi.dsi:fastutil:8.5.6")
|
||||||
|
implementation("net.wesjd:anvilgui:1.7.0-SNAPSHOT")
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#
|
||||||
|
# This file is a part of the SteamWar software.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program 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 Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
LOCAL_CHAT=§eLocal §r{0}§8»§7 {1}
|
||||||
|
|
||||||
|
COMMAND_SYSTEM_ERROR = §cError executing the command!
|
||||||
|
|
||||||
|
SWLISINV_NEXT_PAGE_ACTIVE = §eNext page
|
||||||
|
SWLISINV_NEXT_PAGE_INACTIVE = §7Next page
|
||||||
|
SWLISINV_PREVIOUS_PAGE_ACTIVE = §ePrevious page
|
||||||
|
SWLISINV_PREVIOUS_PAGE_INACTIVE = §7Previous page
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_TITLE={0} selection: {1}
|
||||||
|
SCHEM_SELECTOR_BACK=§eBack
|
||||||
|
SCHEM_SELECTOR_DIR=§9Directory
|
||||||
|
SCHEM_SELECTOR_RANK=§8Rank {0}
|
||||||
|
SCHEM_SELECTOR_OWN=§7Own schematics
|
||||||
|
SCHEM_SELECTOR_PUB=§7Public schematics
|
||||||
|
SCHEM_SELECTOR_SEL_DIR=§7Select directory
|
||||||
|
SCHEM_SELECTOR_NEW_DIR=§7New directory
|
||||||
|
SCHEM_SELECTOR_FILTER=§7Filter
|
||||||
|
SCHEM_SELECTOR_SORTING=§7Order by
|
||||||
|
SCHEM_SELECTOR_SORTING_CURRENT=§7Current: §e{0}
|
||||||
|
SCHEM_SELECTOR_SORTING_NAME=Name
|
||||||
|
SCHEM_SELECTOR_SORTING_TYPE=Schematic type
|
||||||
|
SCHEM_SELECTOR_SORTING_UPDATE=Last update
|
||||||
|
SCHEM_SELECTOR_SORTING_DIRECTION=§e{0} §7order
|
||||||
|
SCHEM_SELECTOR_SORTING_ASC=Ascending
|
||||||
|
SCHEM_SELECTOR_SORTING_DSC=Descending
|
||||||
|
SCHEM_SELECTOR_CLICK_BACK=§7Click to go back
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_ITEM_NAME=§e{0}
|
||||||
|
SCHEM_SELECTOR_ITEM_NAME_FILTER=§7{0}
|
||||||
|
SCHEM_SELECTOR_ITEM_REPLACE=§e{0}§7
|
||||||
|
SCHEM_SELECTOR_ITEM_LORE_TYPE=§7{0}
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_CREATE_DIR_TITLE=Create directory
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE=Filter
|
||||||
|
SCHEM_SELECTOR_FILTER_ENTER_NAME=Insert name
|
||||||
|
SCHEM_SELECTOR_FILTER_NAME=§7Search by name...
|
||||||
|
SCHEM_SELECTOR_FILTER_NAME_SEARCH=§7Search term: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_ENTER_OWNER=Choose owner
|
||||||
|
SCHEM_SELECTOR_FILTER_OWNER=§7Search by owner...
|
||||||
|
SCHEM_SELECTOR_FILTER_OWNER_SEARCH=§7Owner: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_SEL_TYPE=Choose type...
|
||||||
|
SCHEM_SELECTOR_FILTER_TYPE=§7Search by type...
|
||||||
|
SCHEM_SELECTOR_FILTER_TYPE_SEARCH=§7Type: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_MAT=§7Filter by item...
|
||||||
|
SCHEM_SELECTOR_FILTER_MAT_SEARCH=§7Item: §e{0}
|
||||||
|
SCHEM_SELECTOR_CANCEL=§eCancel
|
||||||
|
SCHEM_SELECTOR_GO=§eSearch...
|
||||||
|
SCHEM_SELECTOR_SCHEMATIC=Schematic
|
||||||
|
SCHEM_SELECTOR_DIRECTORY=Directory
|
||||||
|
SCHEM_SELECTOR_SCHEMATIC_NODE=Schematic/Directory
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_SINGLE=§eSingle Filter
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_MULTI=§e{0} Filters
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_EMPTY=§eNo Filters
|
||||||
|
SCHEM_SELECTOR_FILTER_EMPTY=§7Empty
|
||||||
|
|
||||||
|
MATERIAL_SELECTOR_TITLE=Select material
|
||||||
|
|
||||||
|
BAN_TEAM={0} §e{1} §7was §e§lbanned§7 by §e{2} {3}§8: §f{4}
|
||||||
|
BAN_PERMA=§7You are §e§lbanned §epermanently§8: §e{0}
|
||||||
|
BAN_UNTIL=§7You are §e§lbanned §euntil {0}§8: §e{1}
|
||||||
|
UNBAN_ERROR=§cThe player isn't banned.
|
||||||
|
UNBAN=§7You have §e§lunbanned §e{0}.
|
||||||
|
|
||||||
|
MUTE_TEAM={0} §e{1} §7was §e§lmuted§7 by §e{2} {3}§8: §f{4}
|
||||||
|
MUTE_PERMA=§7You are §epermanently §e§lmuted§8: §e{0}
|
||||||
|
MUTE_UNTIL=§7You are §e§lmuted §euntil {0}§8: §e{1}
|
||||||
|
UNMUTE_ERROR=§cThe player isn't muted.
|
||||||
|
UNMUTE=§7You have §e§lunmuted §e{0}.
|
||||||
|
|
||||||
|
NOSCHEMRECEIVING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lrecieving schematics§8: §f{4}
|
||||||
|
NOSCHEMRECEIVING_PERMA=§7You are §epermanently§7 excluded from receiving §e§lschematics§8: §e{0}
|
||||||
|
NOSCHEMRECEIVING_UNTIL=§7You are excluded from receiving §e§lschematics §euntil {0}§8: §e{1}
|
||||||
|
UNNOSCHEMRECEIVING_ERROR=§cThe player is not excluded from receiving schematics.
|
||||||
|
UNNOSCHEMRECEIVING=§e{0} §7may now receive §e§lschematics§7 again§8.
|
||||||
|
|
||||||
|
NOSCHEMSHARING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsharing schematics§8: §f{4}
|
||||||
|
NOSCHEMSHARING_PERMA=§7You are §epermanently§7 excluded from sharing §e§lschematics§8: §e{0}
|
||||||
|
NOSCHEMSHARING_UNTIL=§7You are excluded from sharing §e§lschematics §euntil {0}§8: §e{1}
|
||||||
|
UNNOSCHEMSHARING_ERROR=§cThe player is not excluded from sharing schematics.
|
||||||
|
UNNOSCHEMSHARING=§e{0} §7may now share §e§lschematics§7 again§8.
|
||||||
|
|
||||||
|
NOSCHEMSUBMITTING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsubmitting schematics§8: §f{4}
|
||||||
|
NOSCHEMSUBMITTING_PERMA=§7You are §epermanently§7 excluded from submitting §e§lschematics§8: §e{0}
|
||||||
|
NOSCHEMSUBMITTING_UNTIL=§7You are excluded from submitting §e§lschematics §euntil {0}§8: §e{1}
|
||||||
|
UNNOSCHEMSUBMITTING_ERROR=§cThe player is not excluded from submitting schematics.
|
||||||
|
UNNOSCHEMSUBMITTING=§e{0} §7may now submit §e§lschematics§7 again§8.
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# This file is a part of the SteamWar software.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program 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 Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
LOCAL_CHAT=§eLokal §r{0}§8»§7 {1}
|
||||||
|
|
||||||
|
COMMAND_SYSTEM_ERROR = §cFehler beim Ausführen des Befehls!
|
||||||
|
|
||||||
|
SWLISINV_NEXT_PAGE_ACTIVE = §eSeite vor
|
||||||
|
SWLISINV_NEXT_PAGE_INACTIVE = §7Seite vor
|
||||||
|
SWLISINV_PREVIOUS_PAGE_ACTIVE = §eSeite zurück
|
||||||
|
SWLISINV_PREVIOUS_PAGE_INACTIVE = §7Seite zurück
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_TITLE={0} auswählen: {1}
|
||||||
|
SCHEM_SELECTOR_BACK=§eZurück
|
||||||
|
SCHEM_SELECTOR_DIR=§9Ordner
|
||||||
|
SCHEM_SELECTOR_RANK=§8Rang {0}
|
||||||
|
SCHEM_SELECTOR_OWN=§7Eigene Schematics
|
||||||
|
SCHEM_SELECTOR_PUB=§7Public Schematics
|
||||||
|
SCHEM_SELECTOR_SEL_DIR=§7Ordner auswählen
|
||||||
|
SCHEM_SELECTOR_NEW_DIR=§7Neuer Ordner
|
||||||
|
SCHEM_SELECTOR_FILTER=§7Filter
|
||||||
|
SCHEM_SELECTOR_SORTING=§7Sortierung
|
||||||
|
SCHEM_SELECTOR_SORTING_CURRENT=§7Aktuell: §e{0}
|
||||||
|
SCHEM_SELECTOR_SORTING_NAME=Name
|
||||||
|
SCHEM_SELECTOR_SORTING_TYPE=Schematic-Typ
|
||||||
|
SCHEM_SELECTOR_SORTING_UPDATE=Letztes Update
|
||||||
|
SCHEM_SELECTOR_SORTING_DIRECTION=§7Richtung: §e{0}
|
||||||
|
SCHEM_SELECTOR_SORTING_ASC=Aufsteigend
|
||||||
|
SCHEM_SELECTOR_SORTING_DSC=Absteigend
|
||||||
|
SCHEM_SELECTOR_CLICK_BACK=§7Klicke hier, um zurückzugehen
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_CREATE_DIR_TITLE=Ordner erstellen
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE=Filter
|
||||||
|
SCHEM_SELECTOR_FILTER_ENTER_NAME=Name eingeben
|
||||||
|
SCHEM_SELECTOR_FILTER_NAME=§7Nach Namen suchen...
|
||||||
|
SCHEM_SELECTOR_FILTER_NAME_SEARCH=§7Suchbegriff: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_ENTER_OWNER=Besitzer eingeben
|
||||||
|
SCHEM_SELECTOR_FILTER_OWNER=§7Nach Besitzer suchen...
|
||||||
|
SCHEM_SELECTOR_FILTER_OWNER_SEARCH=§7Besitzer: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_SEL_TYPE=Typ wählen...
|
||||||
|
SCHEM_SELECTOR_FILTER_TYPE=§7Nach Typ filtern...
|
||||||
|
SCHEM_SELECTOR_FILTER_TYPE_SEARCH=§7Typ: §e{0}
|
||||||
|
SCHEM_SELECTOR_FILTER_MAT=§7Nach Item filtern...
|
||||||
|
SCHEM_SELECTOR_FILTER_MAT_SEARCH=§7Item: §e{0}
|
||||||
|
SCHEM_SELECTOR_CANCEL=§eAbbrechen
|
||||||
|
SCHEM_SELECTOR_GO=§eSuchen...
|
||||||
|
SCHEM_SELECTOR_SCHEMATIC=Schematic
|
||||||
|
SCHEM_SELECTOR_DIRECTORY=Ordner
|
||||||
|
SCHEM_SELECTOR_SCHEMATIC_NODE=Schematic/Ordner
|
||||||
|
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_SINGLE=§eEinzelfilter
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_MULTI=§e{0} §e§lMehrfachfilter
|
||||||
|
SCHEM_SELECTOR_FILTER_TITLE_EMPTY=§eKeine Filter
|
||||||
|
SCHEM_SELECTOR_FILTER_EMPTY=§7Leer
|
||||||
|
|
||||||
|
MATERIAL_SELECTOR_TITLE=Material auswählen
|
||||||
|
|
||||||
|
BAN_TEAM={0} §e{1} §7wurde von §e{2} {3} §e§lgebannt§8: §f{4}
|
||||||
|
BAN_PERMA=§7Du bist §epermanent §e§lgebannt§8: §e{0}
|
||||||
|
BAN_UNTIL=§7Du bist §ebis zum {0} §e§lgebannt§8: §e{1}
|
||||||
|
UNBAN_ERROR=§cDer Spieler ist nicht gebannt.
|
||||||
|
UNBAN=§7Du hast §e{0} §e§lentbannt.
|
||||||
|
|
||||||
|
MUTE_TEAM={0} §e{1} §7wurde von §e{2} {3} §e§lgemuted§8: §f{4}
|
||||||
|
MUTE_PERMA=§7Du bist §epermanent §e§lgemuted§8: §e{0}
|
||||||
|
MUTE_UNTIL=§7Du bist §ebis zum {0} §e§lgemuted§8: §e{1}
|
||||||
|
UNMUTE_ERROR=§cDer Spieler ist nicht gemuted.
|
||||||
|
UNMUTE=§7Du hast §e{0} §e§lentmuted.
|
||||||
|
|
||||||
|
NOSCHEMRECEIVING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicerhalten§7 ausgeschlossen§8: §f{4}
|
||||||
|
NOSCHEMRECEIVING_PERMA=§7Du bist §epermanent §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{0}
|
||||||
|
NOSCHEMRECEIVING_UNTIL=§7Du bist §ebis zum {0} §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{1}
|
||||||
|
UNNOSCHEMRECEIVING_ERROR=§cDer Spieler ist nicht vom Erhalten von Schematics ausgeschlossen.
|
||||||
|
UNNOSCHEMRECEIVING=§e{0} §7darf nun wieder §e§lSchematics§7 erhalten§8.
|
||||||
|
|
||||||
|
NOSCHEMSHARING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicverteilen§7 ausgeschlossen§8: §f{4}
|
||||||
|
NOSCHEMSHARING_PERMA=§7Du bist §epermanent §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{0}
|
||||||
|
NOSCHEMSHARING_UNTIL=§7Du bist §ebis zum {0} §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{1}
|
||||||
|
UNNOSCHEMSHARING_ERROR=§cDer Spieler ist nicht vom Verteilen von Schematics ausgeschlossen.
|
||||||
|
UNNOSCHEMSHARING=§e{0} §7darf nun wieder §e§lSchematics§7 verteilen§8.
|
||||||
|
|
||||||
|
NOSCHEMSUBMITTING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematiceinsenden§7 ausgeschlossen§8: §f{4}
|
||||||
|
NOSCHEMSUBMITTING_PERMA=§7Du bist §epermanent §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{0}
|
||||||
|
NOSCHEMSUBMITTING_UNTIL=§7Du bist §ebis zum {0} §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{1}
|
||||||
|
UNNOSCHEMSUBMITTING_ERROR=§cDer Spieler ist nicht vom Einsenden von Schematics ausgeschlossen.
|
||||||
|
UNNOSCHEMSUBMITTING=§e{0} §7darf nun wieder §e§lSchematis§7 einsenden§8.
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
package com.comphenix.tinyprotocol;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import jdk.internal.misc.Unsafe;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An utility class that simplifies reflection in Bukkit plugins.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public final class Reflection {
|
||||||
|
/**
|
||||||
|
* An interface for invoking a specific constructor.
|
||||||
|
*/
|
||||||
|
public interface ConstructorInvoker {
|
||||||
|
/**
|
||||||
|
* Invoke a constructor for a specific class.
|
||||||
|
*
|
||||||
|
* @param arguments - the arguments to pass to the constructor.
|
||||||
|
* @return The constructed object.
|
||||||
|
*/
|
||||||
|
Object invoke(Object... arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for invoking a specific method.
|
||||||
|
*/
|
||||||
|
public interface MethodInvoker {
|
||||||
|
/**
|
||||||
|
* Invoke a method on a specific target object.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static method.
|
||||||
|
* @param arguments - the arguments to pass to the method.
|
||||||
|
* @return The return value, or NULL if is void.
|
||||||
|
*/
|
||||||
|
Object invoke(Object target, Object... arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for retrieving the field content.
|
||||||
|
*
|
||||||
|
* @param <T> - field type.
|
||||||
|
*/
|
||||||
|
public interface FieldAccessor<T> {
|
||||||
|
/**
|
||||||
|
* Retrieve the content of a field.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static field.
|
||||||
|
* @return The value of the field.
|
||||||
|
*/
|
||||||
|
T get(Object target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of a field.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static field.
|
||||||
|
* @param value - the new value of the field.
|
||||||
|
*/
|
||||||
|
void set(Object target, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given object has this field.
|
||||||
|
*
|
||||||
|
* @param target - the object to test.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
boolean hasField(Object target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduce the net.minecraft.server.v* package
|
||||||
|
private static final String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
|
||||||
|
private static final String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||||
|
private static final String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
|
||||||
|
|
||||||
|
// Variable replacement
|
||||||
|
private static final Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
|
||||||
|
|
||||||
|
private Reflection() {
|
||||||
|
// Seal class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param target - the target type.
|
||||||
|
* @param name - the name of the field, or NULL to ignore.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
|
||||||
|
return getField(target, name, fieldType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param name - the name of the field, or NULL to ignore.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
|
||||||
|
return getField(getClass(className), name, fieldType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param target - the target type.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @param index - the number of compatible fields to skip.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
|
||||||
|
return getField(target, null, fieldType, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @param index - the number of compatible fields to skip.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
|
||||||
|
return getField(getClass(className), fieldType, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index, Class<?>... parameters) {
|
||||||
|
return getField(target, null, fieldType, index, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index, Class<?>... parameters) {
|
||||||
|
for (final Field field : target.getDeclaredFields()) {
|
||||||
|
if(matching(field, name, fieldType, parameters) && index-- <= 0) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
return new FieldAccessor<T>() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(Object target) {
|
||||||
|
try {
|
||||||
|
return (T) field.get(target);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot access reflection.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object target, Object value) {
|
||||||
|
try {
|
||||||
|
field.set(target, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot access reflection.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasField(Object target) {
|
||||||
|
// target instanceof DeclaringClass
|
||||||
|
return field.getDeclaringClass().isAssignableFrom(target.getClass());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in parent classes
|
||||||
|
if (target.getSuperclass() != null)
|
||||||
|
return getField(target.getSuperclass(), name, fieldType, index);
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean matching(Field field, String name, Class<T> fieldType, Class<?>... parameters) {
|
||||||
|
if(name != null && !field.getName().equals(name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!fieldType.isAssignableFrom(field.getType()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(parameters.length > 0) {
|
||||||
|
Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
|
||||||
|
|
||||||
|
for(int i = 0; i < parameters.length; i++) {
|
||||||
|
if(arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
|
||||||
|
return getTypedMethod(getClass(className), methodName, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
|
||||||
|
return getTypedMethod(clazz, methodName, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param returnType - the expected return type, or NULL to ignore.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
|
||||||
|
for (final Method method : clazz.getDeclaredMethods()) {
|
||||||
|
if ((methodName == null || method.getName().equals(methodName))
|
||||||
|
&& (returnType == null || method.getReturnType().equals(returnType))
|
||||||
|
&& Arrays.equals(method.getParameterTypes(), params)) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
return (target, arguments) -> {
|
||||||
|
try {
|
||||||
|
return method.invoke(target, arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("Cannot invoke method " + method, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in every superclass
|
||||||
|
if (clazz.getSuperclass() != null)
|
||||||
|
return getTypedMethod(clazz.getSuperclass(), methodName, returnType, params);
|
||||||
|
|
||||||
|
throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this constructor.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
|
||||||
|
return getConstructor(getClass(className), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this constructor.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
|
||||||
|
for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
||||||
|
if (Arrays.equals(constructor.getParameterTypes(), params)) {
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
|
return arguments -> {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("Cannot invoke constructor " + constructor, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class from its full name, without knowing its type on compile time.
|
||||||
|
* <p>
|
||||||
|
* This is useful when looking up fields by a NMS or OBC type.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param lookupName - the class name with variables.
|
||||||
|
* @return The class.
|
||||||
|
*/
|
||||||
|
public static Class<Object> getUntypedClass(String lookupName) {
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
Class<Object> clazz = (Class) getClass(lookupName);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class from its full name.
|
||||||
|
* <p>
|
||||||
|
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
|
||||||
|
* <p>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <th>Variable</th>
|
||||||
|
* <th>Content</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{nms}</td>
|
||||||
|
* <td>Actual package name of net.minecraft.server.VERSION</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{obc}</td>
|
||||||
|
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{version}</td>
|
||||||
|
* <td>The current Minecraft package VERSION, if any.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* @param lookupName - the class name with variables.
|
||||||
|
* @return The looked up class.
|
||||||
|
* @throws IllegalArgumentException If a variable or class could not be found.
|
||||||
|
*/
|
||||||
|
public static Class<?> getClass(String lookupName) {
|
||||||
|
try {
|
||||||
|
return Class.forName(expandVariables(lookupName));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot find " + expandVariables(lookupName), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
|
||||||
|
*
|
||||||
|
* @param name - the full name of the class.
|
||||||
|
* @return The expanded string.
|
||||||
|
*/
|
||||||
|
private static String expandVariables(String name) {
|
||||||
|
StringBuffer output = new StringBuffer();
|
||||||
|
Matcher matcher = MATCH_VARIABLE.matcher(name);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String variable = matcher.group(1);
|
||||||
|
String replacement;
|
||||||
|
|
||||||
|
// Expand all detected variables
|
||||||
|
if (variable.startsWith("nms")) {
|
||||||
|
if(Core.getVersion() >= 17)
|
||||||
|
replacement = "net.minecraft" + variable.substring(3);
|
||||||
|
else
|
||||||
|
replacement = NMS_PREFIX;
|
||||||
|
} else if ("obc".equals(variable))
|
||||||
|
replacement = OBC_PREFIX;
|
||||||
|
else if ("version".equals(variable))
|
||||||
|
replacement = VERSION;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Unknown variable: " + variable);
|
||||||
|
|
||||||
|
// Assume the expanded variables are all packages, and append a dot
|
||||||
|
if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
|
||||||
|
replacement += ".";
|
||||||
|
matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher.appendTail(output);
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static Object newInstance(Class<?> clazz) {
|
||||||
|
try {
|
||||||
|
if (Core.getVersion() > 15) {
|
||||||
|
return Unsafe.getUnsafe().allocateInstance(clazz);
|
||||||
|
} else {
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new SecurityException("Could not create object", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
package com.comphenix.tinyprotocol;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection.FieldAccessor;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.event.server.PluginDisableEvent;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class TinyProtocol implements Listener {
|
||||||
|
|
||||||
|
private static final Class<?> craftServer = Reflection.getClass("{obc}.CraftServer");
|
||||||
|
private static final Class<?> dedicatedPlayerList = Reflection.getClass("{nms.server.dedicated}.DedicatedPlayerList");
|
||||||
|
private static final FieldAccessor<?> getPlayerList = Reflection.getField(craftServer, dedicatedPlayerList, 0);
|
||||||
|
private static final Class<?> playerList = Reflection.getClass("{nms.server.players}.PlayerList");
|
||||||
|
private static final Class<?> minecraftServer = Reflection.getClass("{nms.server}.MinecraftServer");
|
||||||
|
private static final FieldAccessor<?> getMinecraftServer = Reflection.getField(playerList, minecraftServer, 0);
|
||||||
|
public static final Class<?> serverConnection = Reflection.getClass("{nms.server.network}.ServerConnection");
|
||||||
|
private static final FieldAccessor<?> getServerConnection = Reflection.getField(minecraftServer, serverConnection, 0);
|
||||||
|
public static Object getServerConnection(Plugin plugin) {
|
||||||
|
return getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer())));
|
||||||
|
}
|
||||||
|
private static final Class<?> networkManager = Reflection.getClass("{nms.network}.NetworkManager");
|
||||||
|
public static final FieldAccessor<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager);
|
||||||
|
|
||||||
|
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
|
||||||
|
private static int id = 0;
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
//enforce init
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
|
private final String handlerName;
|
||||||
|
private final List<?> connections;
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
|
||||||
|
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
|
||||||
|
|
||||||
|
private TinyProtocol(final Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.handlerName = "tiny-" + plugin.getName() + "-" + ++id;
|
||||||
|
this.connections = networkManagers.get(getServerConnection(plugin));
|
||||||
|
|
||||||
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
|
|
||||||
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
|
new PacketInterceptor(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerLogin(PlayerLoginEvent e) {
|
||||||
|
if(closed)
|
||||||
|
return;
|
||||||
|
new PacketInterceptor(e.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onPlayerDisconnect(PlayerQuitEvent e) {
|
||||||
|
getInterceptor(e.getPlayer()).ifPresent(PacketInterceptor::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPluginDisable(PluginDisableEvent e) {
|
||||||
|
if (e.getPlugin().equals(plugin)) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
|
||||||
|
packetFilters.computeIfAbsent(packetType, c -> Collections.synchronizedList(new ArrayList<>(1))).add(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
|
||||||
|
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacket(Player player, Object packet) {
|
||||||
|
getInterceptor(player).ifPresent(i -> i.sendPacket(packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receivePacket(Player player, Object packet) {
|
||||||
|
getInterceptor(player).ifPresent(i -> i.receivePacket(packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void close() {
|
||||||
|
if(closed)
|
||||||
|
return;
|
||||||
|
closed = true;
|
||||||
|
|
||||||
|
HandlerList.unregisterAll(this);
|
||||||
|
|
||||||
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
|
getInterceptor(player).ifPresent(PacketInterceptor::close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<PacketInterceptor> getInterceptor(Player player) {
|
||||||
|
synchronized (playerInterceptors) {
|
||||||
|
return Optional.ofNullable(playerInterceptors.get(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManager, Channel.class, 0);
|
||||||
|
private static final FieldAccessor<UUID> getUUID = Reflection.getField(networkManager, UUID.class, 0);
|
||||||
|
|
||||||
|
private final class PacketInterceptor extends ChannelDuplexHandler {
|
||||||
|
private final Player player;
|
||||||
|
private final Channel channel;
|
||||||
|
|
||||||
|
private PacketInterceptor(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
|
||||||
|
channel = getChannel.get(connections.stream().filter(connection -> player.getUniqueId().equals(getUUID.get(connection))).findAny().orElseThrow(() -> {
|
||||||
|
player.kickPlayer("An injection failure happend.");
|
||||||
|
return new SecurityException("Could not find channel for player " + player.getName());
|
||||||
|
}));
|
||||||
|
|
||||||
|
if(!channel.isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized (playerInterceptors) {
|
||||||
|
playerInterceptors.put(player, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.pipeline().addBefore("packet_handler", handlerName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPacket(Object packet) {
|
||||||
|
channel.pipeline().writeAndFlush(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receivePacket(Object packet) {
|
||||||
|
channel.pipeline().context("encoder").fireChannelRead(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
if(channel.isActive()) {
|
||||||
|
channel.eventLoop().execute(() -> {
|
||||||
|
try {
|
||||||
|
channel.pipeline().remove(handlerName);
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (playerInterceptors) {
|
||||||
|
playerInterceptors.remove(player, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
try {
|
||||||
|
msg = filterPacket(player, msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != null) {
|
||||||
|
super.channelRead(ctx, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
try {
|
||||||
|
msg = filterPacket(player, msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != null) {
|
||||||
|
super.write(ctx, msg, promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object filterPacket(Player player, Object packet) {
|
||||||
|
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
|
||||||
|
|
||||||
|
for(BiFunction<Player, Object, Object> filter : filters) {
|
||||||
|
packet = filter.apply(player, packet);
|
||||||
|
|
||||||
|
if(packet == null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||||
|
|
||||||
|
public class CaseInsensitiveCommandsListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||||
|
String[] strings = event.getMessage().split(" ");
|
||||||
|
strings[0] = strings[0].toLowerCase();
|
||||||
|
event.setMessage(String.join(" ", strings));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandMap;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
class CommandRegistering {
|
||||||
|
|
||||||
|
private static final CommandMap commandMap;
|
||||||
|
private static final Map<String, Command> knownCommandMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
final Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||||
|
commandMapField.setAccessible(true);
|
||||||
|
commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException exception) {
|
||||||
|
Bukkit.shutdown();
|
||||||
|
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||||
|
knownCommandsField.setAccessible(true);
|
||||||
|
knownCommandMap = (Map<String, Command>) knownCommandsField.get(commandMap);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException exception) {
|
||||||
|
Bukkit.shutdown();
|
||||||
|
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unregister(Command command) {
|
||||||
|
knownCommandMap.remove(command.getName());
|
||||||
|
command.getAliases().forEach(knownCommandMap::remove);
|
||||||
|
command.unregister(commandMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register(Command command) {
|
||||||
|
commandMap.register("steamwar", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.message.Message;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class SWCommand extends AbstractSWCommand<CommandSender> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
TypeUtils.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Command command;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private Message message = null;
|
||||||
|
private List<String> defaultHelpMessages = new ArrayList<>();
|
||||||
|
|
||||||
|
protected SWCommand(String command) {
|
||||||
|
super(CommandSender.class, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SWCommand(String command, String... aliases) {
|
||||||
|
super(CommandSender.class, command, aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createAndSafeCommand(String command, String[] aliases) {
|
||||||
|
this.command = new Command(command, "", "/" + command, Arrays.asList(aliases)) {
|
||||||
|
@Override
|
||||||
|
public boolean execute(CommandSender sender, String alias, String[] args) {
|
||||||
|
SWCommand.this.execute(sender, alias, args);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
||||||
|
return SWCommand.this.tabComplete(sender, alias, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregister() {
|
||||||
|
CommandRegistering.unregister(this.command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register() {
|
||||||
|
CommandRegistering.register(this.command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void commandSystemError(CommandSender sender, CommandFrameworkException e) {
|
||||||
|
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
Core.MESSAGE.sendPrefixless("COMMAND_SYSTEM_ERROR", sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void commandSystemWarning(Supplier<String> message) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDefaultHelpMessage(String message) {
|
||||||
|
defaultHelpMessages.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendMessage(CommandSender sender, String message, Object[] args) {
|
||||||
|
this.message.send(message, sender, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initialisePartOf(AbstractSWCommand parent) {
|
||||||
|
if (parent instanceof SWCommand) {
|
||||||
|
this.message = ((SWCommand) parent).message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Register(help = true)
|
||||||
|
public void internalHelp(Player p, String... args) {
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
message.sendPrefixless("COMMAND_HELP_HEAD", p, command.getName().substring(0, 1).toUpperCase() + command.getName().substring(1));
|
||||||
|
defaultHelpMessages.forEach(s -> message.sendPrefixless(s, p));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Failed to send help message", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AtomicInteger atomicInteger = new AtomicInteger();
|
||||||
|
if (args.length != 0) {
|
||||||
|
commandList.forEach(subCommand -> {
|
||||||
|
List<String> tabCompletes = subCommand.tabComplete(p, args);
|
||||||
|
if (tabCompletes == null || tabCompletes.isEmpty()) {
|
||||||
|
atomicInteger.incrementAndGet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean hasTabCompletes = tabCompletes.stream()
|
||||||
|
.anyMatch(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase()));
|
||||||
|
if (hasTabCompletes) {
|
||||||
|
send(p, subCommand);
|
||||||
|
} else {
|
||||||
|
atomicInteger.incrementAndGet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (args.length == 0 || atomicInteger.get() == commandList.size()) {
|
||||||
|
commandList.forEach(subCommand -> {
|
||||||
|
if (subCommand.validator == null || subCommand.validator.validate(p, p, (s, args1) -> {})) {
|
||||||
|
send(p, subCommand);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send(Player p, SubCommand<CommandSender> subCommand) {
|
||||||
|
try {
|
||||||
|
for (String s : subCommand.description) {
|
||||||
|
String hover = "§8/§e" + command.getName() + " " + String.join(" ", subCommand.subCommand);
|
||||||
|
String suggest = "/" + command.getName() + " " + String.join(" ", subCommand.subCommand);
|
||||||
|
message.sendPrefixless(s, p, hover, new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, suggest));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Failed to send description of registered method '" + subCommand.method + "' with description '" + subCommand.description + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface TypeMapper<T> extends AbstractTypeMapper<CommandSender, T> {
|
||||||
|
/**
|
||||||
|
* The CommandSender can be null!
|
||||||
|
*/
|
||||||
|
default T map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||||
|
return map(previousArguments, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backwards compatibility, can be removed later on
|
||||||
|
// SINCE="Use the other map Function without calling super!"
|
||||||
|
@Deprecated
|
||||||
|
default T map(String[] previousArguments, String s) {
|
||||||
|
throw new SecurityException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import de.steamwar.sql.BauweltMember;
|
||||||
|
import de.steamwar.sql.SchematicNode;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class TypeUtils {
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
SWCommandUtils.addMapper(Player.class, SWCommandUtils.createMapper(Bukkit::getPlayer, s -> Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList())));
|
||||||
|
SWCommandUtils.addMapper(GameMode.class, SWCommandUtils.createMapper(s -> {
|
||||||
|
s = s.toLowerCase();
|
||||||
|
if (s.equals("s") || s.equals("survival") || s.equals("0")) return GameMode.SURVIVAL;
|
||||||
|
if (s.equals("c") || s.equals("creative") || s.equals("1")) return GameMode.CREATIVE;
|
||||||
|
if (s.equals("sp") || s.equals("spectator") || s.equals("3")) return GameMode.SPECTATOR;
|
||||||
|
if (s.equals("a") || s.equals("adventure") || s.equals("2")) return GameMode.ADVENTURE;
|
||||||
|
return null;
|
||||||
|
}, s -> Arrays.asList("s", "survival", "0", "c", "creative", "1", "sp", "spectator", "3", "a", "adventure", "2")));
|
||||||
|
SWCommandUtils.addMapper(SteamwarUser.class, SWCommandUtils.createMapper(SteamwarUser::get, s -> Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList())));
|
||||||
|
SWCommandUtils.addMapper(SchematicNode.class, new TypeMapper<SchematicNode>() {
|
||||||
|
@Override
|
||||||
|
public SchematicNode map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||||
|
return SchematicNode.getNodeFromPath(SteamwarUser.get(((Player) commandSender).getUniqueId()), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> tabCompletes(CommandSender sender, String[] previousArguments, String s) {
|
||||||
|
return SchematicNode.getNodeTabcomplete(SteamwarUser.get(((Player) sender).getUniqueId()), s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
SWCommandUtils.addMapper(BauweltMember.class, new TypeMapper<BauweltMember>() {
|
||||||
|
@Override
|
||||||
|
public BauweltMember map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||||
|
if (!(commandSender instanceof Player)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Player player = (Player) commandSender;
|
||||||
|
return BauweltMember.getMembers(player.getUniqueId())
|
||||||
|
.stream()
|
||||||
|
.filter(member -> SteamwarUser.get(member.getMemberID()).getUserName().equalsIgnoreCase(s))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> tabCompletes(CommandSender sender, String[] previousArguments, String s) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
Player player = (Player) sender;
|
||||||
|
return BauweltMember.getMembers(player.getUniqueId())
|
||||||
|
.stream()
|
||||||
|
.map(m -> SteamwarUser.get(m.getMemberID()).getUserName())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.command;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
public interface TypeValidator<T> extends AbstractValidator<CommandSender, T> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class BountifulWrapper {
|
||||||
|
private BountifulWrapper() {}
|
||||||
|
|
||||||
|
public static final IBountifulWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
public interface IBountifulWrapper {
|
||||||
|
void playPling(Player player);
|
||||||
|
|
||||||
|
void sendMessage(Player player, ChatMessageType type, BaseComponent... msg);
|
||||||
|
|
||||||
|
Object getDataWatcherObject(int index, Class<?> type);
|
||||||
|
Object getDataWatcherItem(Object dataWatcherObject, Object value);
|
||||||
|
|
||||||
|
PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset);
|
||||||
|
PositionSetter getRelMoveSetter(Class<?> packetClass);
|
||||||
|
UUIDSetter getUUIDSetter(Class<?> packetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface PositionSetter {
|
||||||
|
void set(Object packet, double x, double y, double z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface UUIDSetter {
|
||||||
|
void set(Object packet, UUID uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
public interface ChatWrapper {
|
||||||
|
ChatWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
|
||||||
|
Object stringToChatComponent(String text);
|
||||||
|
|
||||||
|
Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues);
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import com.viaversion.viaversion.api.Via;
|
||||||
|
import de.steamwar.sql.BauweltMember;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.eclipse.openj9.criu.CRIUSupport;
|
||||||
|
import org.eclipse.openj9.criu.JVMCRIUException;
|
||||||
|
import sun.misc.Signal;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class CheckpointUtils {
|
||||||
|
private CheckpointUtils() {}
|
||||||
|
|
||||||
|
public static void signalHandler() {
|
||||||
|
Signal.handle(new Signal("USR1"), signal -> Bukkit.getScheduler().runTask(Core.getInstance(), CheckpointUtils::freeze));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void freeze() {
|
||||||
|
String checkpointFile = System.getProperty("checkpoint");
|
||||||
|
if(!CRIUSupport.isCheckpointAllowed() || checkpointFile == null) {
|
||||||
|
Bukkit.shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getOnlinePlayers().forEach(player -> player.kickPlayer(null));
|
||||||
|
|
||||||
|
List<?> networkManagers = TinyProtocol.networkManagers.get(TinyProtocol.getServerConnection(Core.getInstance()));
|
||||||
|
if(!Bukkit.getOnlinePlayers().isEmpty() || !networkManagers.isEmpty()) {
|
||||||
|
Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing");
|
||||||
|
Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path = FileSystems.getDefault().getPath(checkpointFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
freezeInternal(path);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String message = e.getMessage() != null ? e.getMessage() : "";
|
||||||
|
if(message.contains("Connected TCP socket")) {
|
||||||
|
Core.getInstance().getLogger().log(Level.INFO, "Connected TCP socket, waiting for checkpointing");
|
||||||
|
Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.shutdown();
|
||||||
|
|
||||||
|
if(!message.contains("Can't dump ghost file") && !message.contains("Can't create link remap")) // File/Jar has been updated
|
||||||
|
throw new SecurityException(e);
|
||||||
|
} finally {
|
||||||
|
// Delete checkpoint
|
||||||
|
try (Stream<Path> stream = Files.walk(path)) {
|
||||||
|
stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
|
||||||
|
} catch (IOException e) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<List> channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class);
|
||||||
|
private static final Reflection.MethodInvoker bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class);
|
||||||
|
private static void freezeInternal(Path path) throws Exception {
|
||||||
|
Bukkit.getWorlds().forEach(FlatteningWrapper.impl::syncSave);
|
||||||
|
Statement.closeAll();
|
||||||
|
|
||||||
|
// Close socket
|
||||||
|
Via.getManager().getInjector().uninject();
|
||||||
|
Object serverConnection = TinyProtocol.getServerConnection(Core.getInstance());
|
||||||
|
List<?> channels = channelFutures.get(serverConnection);
|
||||||
|
for(Object future : channels) {
|
||||||
|
((ChannelFuture) future).channel().close().syncUninterruptibly();
|
||||||
|
}
|
||||||
|
channels.clear();
|
||||||
|
|
||||||
|
System.runFinalization();
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
// Do the checkpoint
|
||||||
|
path.toFile().mkdirs();
|
||||||
|
CRIUSupport criu = new CRIUSupport(path);
|
||||||
|
criu.setAutoDedup(true);
|
||||||
|
criu.setFileLocks(true);
|
||||||
|
criu.setShellJob(true);
|
||||||
|
criu.setLogFile("criu.log");
|
||||||
|
try {
|
||||||
|
criu.checkpointJVM();
|
||||||
|
} catch (JVMCRIUException e) {
|
||||||
|
Path logfile = path.resolve("criu.log");
|
||||||
|
if(logfile.toFile().exists())
|
||||||
|
throw new IllegalStateException("Could not create checkpoint, criu log:\n" + new String(Files.readAllBytes(logfile)), e);
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get new port
|
||||||
|
int port;
|
||||||
|
try (DataInputStream stream = new DataInputStream(Files.newInputStream(path.resolve("port").toFile().toPath()))) {
|
||||||
|
port = stream.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen socket
|
||||||
|
bind.invoke(serverConnection, InetAddress.getLoopbackAddress(), port);
|
||||||
|
if(Core.getVersion() > 12) {
|
||||||
|
for(Object future : channels) {
|
||||||
|
((ChannelFuture) future).channel().config().setAutoRead(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Via.getManager().getInjector().inject();
|
||||||
|
|
||||||
|
BauweltMember.clear();
|
||||||
|
Core.getInstance().getLogger().log(Level.INFO, "Checkpoint restored");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class CommandRemover {
|
||||||
|
private static final Reflection.FieldAccessor<SimpleCommandMap> commandMap = Reflection.getField("{obc}.CraftServer", "commandMap", SimpleCommandMap.class);
|
||||||
|
private static final Reflection.FieldAccessor<Map> knownCommands = Reflection.getField(SimpleCommandMap.class, "knownCommands", Map.class);
|
||||||
|
public static void removeAll(String... cmds) {
|
||||||
|
Map<String, Command> knownCmds = knownCommands.get(commandMap.get(Bukkit.getServer()));
|
||||||
|
for (String cmd : cmds) {
|
||||||
|
knownCmds.remove(cmd.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import de.steamwar.command.*;
|
||||||
|
import de.steamwar.core.authlib.AuthlibInjector;
|
||||||
|
import de.steamwar.core.events.*;
|
||||||
|
import de.steamwar.message.Message;
|
||||||
|
import de.steamwar.network.NetworkReceiver;
|
||||||
|
import de.steamwar.network.handlers.ServerDataHandler;
|
||||||
|
import de.steamwar.sql.SchematicNode;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class Core extends JavaPlugin{
|
||||||
|
|
||||||
|
public static final Message MESSAGE = new Message("SpigotCore", Core.class.getClassLoader());
|
||||||
|
|
||||||
|
private static final int VERSION = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().split("_", 3)[1]);
|
||||||
|
|
||||||
|
public static int getVersion(){
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JavaPlugin instance;
|
||||||
|
public static JavaPlugin getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
public static void setInstance(JavaPlugin instance) {
|
||||||
|
Core.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorHandler errorHandler;
|
||||||
|
private CrashDetector crashDetector;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
setInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
errorHandler = new ErrorHandler();
|
||||||
|
crashDetector = new CrashDetector();
|
||||||
|
|
||||||
|
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, CommandSender, Object>) (mapper, tabCompleter) -> new TypeMapper<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||||
|
return mapper.apply(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> tabCompletes(CommandSender sender, String[] previousArguments, String s) {
|
||||||
|
return tabCompleter.apply(sender, s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for(Listener listener : new Listener[]{new CaseInsensitiveCommandsListener(), new PlayerJoinedEvent(), new ChattingEvent(), new WorldLoadEvent(), RecipeDiscoverWrapper.impl}) {
|
||||||
|
getServer().getPluginManager().registerEvents(listener, this);
|
||||||
|
}
|
||||||
|
if(!Statement.productionDatabase()) {
|
||||||
|
getServer().getPluginManager().registerEvents(LocaleChangeWrapper.impl, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getServer().getMessenger().registerIncomingPluginChannel(this, "sw:bridge", new NetworkReceiver());
|
||||||
|
getServer().getMessenger().registerOutgoingPluginChannel(this, "sw:bridge");
|
||||||
|
|
||||||
|
if(Core.getVersion() < 19)
|
||||||
|
AuthlibInjector.inject();
|
||||||
|
|
||||||
|
TinyProtocol.init();
|
||||||
|
CheckpointUtils.signalHandler();
|
||||||
|
new AntiNocom();
|
||||||
|
|
||||||
|
if(Core.getVersion() < 17 && Bukkit.getPluginManager().getPlugin("ViaVersion") != null)
|
||||||
|
new PartialChunkFixer();
|
||||||
|
|
||||||
|
if(Core.getVersion() >= 19)
|
||||||
|
new ServerDataHandler();
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskTimer(this, TabCompletionCache::invalidateOldEntries, 20, 20);
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SteamwarUser::clear, 72000, 72000);
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SchematicNode::clear, 20L * 30, 20L * 30);
|
||||||
|
|
||||||
|
try {
|
||||||
|
getLogger().log(Level.INFO, "Running on: " + new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream())).readLine());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Could not load Hostname", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
TinyProtocol.instance.close();
|
||||||
|
errorHandler.unregister();
|
||||||
|
if(crashDetector.onMainThread())
|
||||||
|
Statement.closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class CraftbukkitWrapper {
|
||||||
|
private CraftbukkitWrapper() {}
|
||||||
|
|
||||||
|
public static final ICraftbukkitWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
public interface ICraftbukkitWrapper {
|
||||||
|
void sendChunk(Player p, int chunkX, int chunkZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import de.steamwar.sql.SWException;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class CrashDetector {
|
||||||
|
|
||||||
|
private static final long TIMEOUT = 20_000_000_000L;
|
||||||
|
private final AtomicLong lastTick = new AtomicLong(Long.MAX_VALUE);
|
||||||
|
private final AtomicLong lastMessage = new AtomicLong(Long.MAX_VALUE);
|
||||||
|
private final Thread mainThread = Thread.currentThread();
|
||||||
|
private final Thread watchdog;
|
||||||
|
|
||||||
|
private boolean run = true;
|
||||||
|
|
||||||
|
public CrashDetector () {
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
|
||||||
|
lastTick.set(System.nanoTime());
|
||||||
|
lastMessage.set(System.nanoTime());
|
||||||
|
}, 0, 1);
|
||||||
|
watchdog = new Thread(this::run, "SteamWar Watchdog");
|
||||||
|
watchdog.setDaemon(true);
|
||||||
|
watchdog.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
run = false;
|
||||||
|
watchdog.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onMainThread() {
|
||||||
|
return Thread.currentThread() == mainThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run() {
|
||||||
|
SWException.init();
|
||||||
|
while (run) {
|
||||||
|
long curTime = System.nanoTime();
|
||||||
|
if(curTime - 4*TIMEOUT >= lastTick.get()) {
|
||||||
|
SWException.log("Server did not recover in " + ((curTime - lastTick.get()) / 1000000.0) + "ms, unclean server stop", "");
|
||||||
|
hardStop();
|
||||||
|
} else if(curTime - TIMEOUT > lastMessage.get()) {
|
||||||
|
if(mainThread.isAlive()) {
|
||||||
|
SWException.log("Server hung for " + ((curTime - lastTick.get()) / 1000000.0) + "ms", Arrays.stream(mainThread.getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("\n")));
|
||||||
|
} else {
|
||||||
|
SWException.log("Server thread already dead, unclean server stop", "Core enabled: " + Core.getInstance().isEnabled());
|
||||||
|
hardStop();
|
||||||
|
}
|
||||||
|
lastMessage.set(curTime);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hardStop() {
|
||||||
|
if(Core.getInstance().isEnabled()) {
|
||||||
|
Core.getInstance().onDisable();
|
||||||
|
}
|
||||||
|
Statement.closeAll();
|
||||||
|
//System.exit(0); Does freeze, potential freezing issues: ConsoleRestoreHook, ApplicationShutdownHooks or DeleteOnExitHook
|
||||||
|
Runtime.getRuntime().halt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import de.steamwar.sql.SWException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class ErrorHandler extends Handler {
|
||||||
|
|
||||||
|
private final long watchdogThreadId;
|
||||||
|
|
||||||
|
public ErrorHandler(){
|
||||||
|
Logger.getLogger("").addHandler(this);
|
||||||
|
|
||||||
|
Class<?> watchdogThread = Reflection.getClass("org.spigotmc.WatchdogThread");
|
||||||
|
Reflection.FieldAccessor<?> getInstance = Reflection.getField(watchdogThread, watchdogThread, 0);
|
||||||
|
Thread watchdog = (Thread) getInstance.get(null);
|
||||||
|
watchdogThreadId = watchdog.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregister() {
|
||||||
|
Logger.getLogger("").removeHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void publish(LogRecord logRecord) {
|
||||||
|
if(logRecord.getLevel().intValue() < Level.WARNING.intValue())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(watchdogThreadId == logRecord.getThreadID())
|
||||||
|
return;
|
||||||
|
|
||||||
|
String message = logRecord.getMessage() != null ? logRecord.getMessage() : "";
|
||||||
|
for(String reason : ignoreStartsWith)
|
||||||
|
if(message.startsWith(reason))
|
||||||
|
return;
|
||||||
|
for(String reason : ignoreContains)
|
||||||
|
if(message.contains(reason))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
|
||||||
|
if(logRecord.getThrown() != null)
|
||||||
|
logRecord.getThrown().printStackTrace(new PrintStream(stacktraceOutput));
|
||||||
|
String stacktrace = stacktraceOutput.toString();
|
||||||
|
if(stacktrace.contains("POI data mismatch") || stacktrace.contains("Newer version! Server downgrades are not supported!"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(message.isEmpty() && stacktrace.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
SWException.log(message, stacktrace);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Core.getInstance().getLogger().log(Level.INFO, "Could not log error in database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
//This is task of the database
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws SecurityException {
|
||||||
|
//Done in the database
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<String> ignoreStartsWith;
|
||||||
|
private static final List<String> ignoreContains;
|
||||||
|
|
||||||
|
static {
|
||||||
|
List<String> startsWith = new ArrayList<>();
|
||||||
|
startsWith.add("Could not save the list after adding a user.");
|
||||||
|
startsWith.add("Could not save spigot.yml");
|
||||||
|
startsWith.add("Failed to save operators list:");
|
||||||
|
startsWith.add("Block at");
|
||||||
|
startsWith.add("POI data mismatch");
|
||||||
|
startsWith.add("handleDisconnection() called twice");
|
||||||
|
startsWith.add("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
|
||||||
|
startsWith.add("The server will make no attempt to authenticate usernames. Beware.");
|
||||||
|
startsWith.add("Whilst this makes it possible to use BungeeCord,");
|
||||||
|
startsWith.add("Please see http://www.spigotmc.org/wiki/firewall-guide/ for further information.");
|
||||||
|
startsWith.add("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
|
||||||
|
startsWith.add("This crash report has been saved to:");
|
||||||
|
startsWith.add("Could not pass event PlayerQuitEvent to WorldEditSUI");
|
||||||
|
startsWith.add("[ViaVersion] ");
|
||||||
|
startsWith.add("[ViaBackwards] ");
|
||||||
|
startsWith.add("Something went wrong upgrading!");
|
||||||
|
startsWith.add("Tried to load unrecognized recipe");
|
||||||
|
startsWith.add("Invalid BlockState in palette:");
|
||||||
|
startsWith.add("Could not register alias");
|
||||||
|
startsWith.add("Can't keep up! Is the server overloaded?");
|
||||||
|
startsWith.add("\tat ");
|
||||||
|
startsWith.add("java.lang.Exception");
|
||||||
|
startsWith.add("An exceptionCaught()");
|
||||||
|
startsWith.add("Exception verifying");
|
||||||
|
startsWith.add("[WorldEditSUI]");
|
||||||
|
startsWith.add("Unsupported key:");
|
||||||
|
startsWith.add("ThrownPotion entity");
|
||||||
|
startsWith.add("Couldn't load custom particle");
|
||||||
|
startsWith.add("Chunk file at [");
|
||||||
|
startsWith.add("Ignoring unknown attribute");
|
||||||
|
startsWith.add("Skipping player strafe phase because no player was found");
|
||||||
|
startsWith.add("Couldn't save chunk; already in use by another instance of Minecraft?");
|
||||||
|
startsWith.add("Failed to save player data for ");
|
||||||
|
startsWith.add("Failed to check session lock for world located at");
|
||||||
|
startsWith.add("Saving oversized chunk ");
|
||||||
|
startsWith.add("Ignoring plugin channel");
|
||||||
|
startsWith.add("Ignoring incoming plugin");
|
||||||
|
startsWith.add("This version of Minecraft is extremely outdated");
|
||||||
|
startsWith.add("Custom worlds heights are NOT SUPPORTED for 1.16 players and older");
|
||||||
|
startsWith.add("You have min/max set to -64/384");
|
||||||
|
startsWith.add("You have min/max set to -64/256");
|
||||||
|
startsWith.add("This version of ViaBackwards does not fully support 1.19 servers.");
|
||||||
|
startsWith.add("Biome with id");
|
||||||
|
startsWith.add("1.16 and 1.16.1 clients are only partially supported");
|
||||||
|
startsWith.add("Unable to parse CustomName from ");
|
||||||
|
startsWith.add("java.util.ConcurrentModificationException");
|
||||||
|
startsWith.add("com.destroystokyo.paper.exception.ServerInternalException: Attempted to place a tile entity");
|
||||||
|
startsWith.add("World: minecraft:overworld");
|
||||||
|
startsWith.add("Chunk coordinates: ");
|
||||||
|
startsWith.add("Failed to save history");
|
||||||
|
startsWith.add("\t... ");
|
||||||
|
startsWith.add("ERROR IN Protocol");
|
||||||
|
ignoreStartsWith = Collections.unmodifiableList(startsWith);
|
||||||
|
|
||||||
|
List<String> contains = new ArrayList<>();
|
||||||
|
contains.add("moved too quickly!");
|
||||||
|
contains.add("moved wrongly!");
|
||||||
|
contains.add("was kicked for floating too long!");
|
||||||
|
contains.add("just tried to change non-editable sign");
|
||||||
|
contains.add("about their usage of System.out/err.print");
|
||||||
|
ignoreContains = Collections.unmodifiableList(contains);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
public class FlatteningWrapper {
|
||||||
|
private FlatteningWrapper() {}
|
||||||
|
|
||||||
|
public static final Class<?> scoreboardObjective = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutScoreboardObjective");
|
||||||
|
public static final Class<?> scoreboardScore = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutScoreboardScore");
|
||||||
|
|
||||||
|
public static final IFlatteningWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
public interface IFlatteningWrapper {
|
||||||
|
void setScoreboardTitle(Object packet, String title);
|
||||||
|
void setScoreAction(Object packet);
|
||||||
|
|
||||||
|
Material getMaterial(String material);
|
||||||
|
Material getDye(int colorCode);
|
||||||
|
ItemStack setSkullOwner(String player);
|
||||||
|
|
||||||
|
Object getPose(EntityPose pose);
|
||||||
|
void setNamedSpawnPacketDataWatcher(Object packet);
|
||||||
|
Object formatDisplayName(String displayName);
|
||||||
|
|
||||||
|
void setSpawnPacketType(Object packet, EntityType type);
|
||||||
|
|
||||||
|
int getViewDistance(Player player);
|
||||||
|
|
||||||
|
void syncSave(World world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EntityPose {
|
||||||
|
NORMAL,
|
||||||
|
SNEAKING,
|
||||||
|
SWIMMING;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
|
public interface LocaleChangeWrapper extends Listener {
|
||||||
|
LocaleChangeWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
public interface ProtocolWrapper {
|
||||||
|
|
||||||
|
Class<?> itemStack = Reflection.getClass("{nms.world.item}.ItemStack");
|
||||||
|
Class<?> spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity");
|
||||||
|
Class<?> spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntityLiving");
|
||||||
|
Class<?> equipmentPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityEquipment");
|
||||||
|
|
||||||
|
Class<?> enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode");
|
||||||
|
Reflection.MethodInvoker getGameModeById = Reflection.getTypedMethod(enumGamemode, null, enumGamemode, int.class);
|
||||||
|
|
||||||
|
// 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head
|
||||||
|
Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("{nms.world.entity}.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4};
|
||||||
|
|
||||||
|
ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
void setEquipmentPacketStack(Object packet, Object slot, Object stack);
|
||||||
|
|
||||||
|
Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode);
|
||||||
|
|
||||||
|
default void initTPSWarp(LongSupplier longSupplier) {
|
||||||
|
Class systemUtils = Reflection.getClass("{nms}.SystemUtils");
|
||||||
|
Reflection.getField(systemUtils, LongSupplier.class, 0).set(systemUtils, (LongSupplier) () -> System.nanoTime() + longSupplier.getAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PlayerInfoAction {
|
||||||
|
ADD,
|
||||||
|
GAMEMODE,
|
||||||
|
REMOVE
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
|
public interface RecipeDiscoverWrapper extends Listener {
|
||||||
|
RecipeDiscoverWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class TPSWarpUtils {
|
||||||
|
|
||||||
|
private static long nanoOffset = 0;
|
||||||
|
private static long nanoDOffset = 0;
|
||||||
|
private static BukkitTask bukkitTask = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ProtocolWrapper.impl.initTPSWarp(() -> nanoOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warp(double tps) {
|
||||||
|
double d = 50 - (50 / (tps / 20.0));
|
||||||
|
nanoDOffset = Math.max(0, (long) (d * 1000000));
|
||||||
|
if (nanoDOffset == 0) {
|
||||||
|
if (bukkitTask == null) return;
|
||||||
|
bukkitTask.cancel();
|
||||||
|
bukkitTask = null;
|
||||||
|
} else if (bukkitTask == null) {
|
||||||
|
bukkitTask = Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> nanoOffset += nanoDOffset, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWarping() {
|
||||||
|
return nanoDOffset > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
public class TPSWatcher {
|
||||||
|
|
||||||
|
private static final double TICK_DEFAULT = 20.0;
|
||||||
|
|
||||||
|
private static final TPSWatcher tps_OneSecond = new TPSWatcher(1000);
|
||||||
|
private static final TPSWatcher tps_TenSecond = new TPSWatcher(10000);
|
||||||
|
|
||||||
|
private long lastTime = System.currentTimeMillis();
|
||||||
|
private double tps = TICK_DEFAULT;
|
||||||
|
|
||||||
|
private TPSWatcher(long timeInterval) {
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (currentTime > lastTime)
|
||||||
|
tps = (timeInterval / (double) (currentTime - lastTime)) * TICK_DEFAULT;
|
||||||
|
|
||||||
|
lastTime = currentTime;
|
||||||
|
}, timeInterval / 50, timeInterval / 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPS() {
|
||||||
|
return getTPS(TPSType.ONE_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPS(double limit) {
|
||||||
|
return getTPS(TPSType.ONE_SECOND, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPSUnlimited() {
|
||||||
|
return getTPSUnlimited(TPSType.ONE_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPS(TPSType tpsType) {
|
||||||
|
return getTPS(tpsType, TICK_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPS(TPSType tpsType, double limit) {
|
||||||
|
return Math.min(getTPSUnlimited(tpsType), limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getTPSUnlimited(TPSType tpsType) {
|
||||||
|
switch (tpsType) {
|
||||||
|
case TEN_SECONDS:
|
||||||
|
return round(tps_TenSecond.tps);
|
||||||
|
case ONE_MINUTE:
|
||||||
|
return round(getSpigotTPS()[0]);
|
||||||
|
case FIVE_MINUTES:
|
||||||
|
return round(getSpigotTPS()[1]);
|
||||||
|
case TEN_MINUTES:
|
||||||
|
return round(getSpigotTPS()[2]);
|
||||||
|
case ONE_SECOND:
|
||||||
|
default:
|
||||||
|
return round(tps_OneSecond.tps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> minecraftServer = Reflection.getClass("{nms.server}.MinecraftServer");
|
||||||
|
private static final Reflection.MethodInvoker getServer = Reflection.getTypedMethod(minecraftServer, "getServer", minecraftServer);
|
||||||
|
private static final Reflection.FieldAccessor<double[]> recentTps = Reflection.getField(minecraftServer, "recentTps", double[].class);
|
||||||
|
private static double[] getSpigotTPS() {
|
||||||
|
return recentTps.get(getServer.invoke(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double round(double d) {
|
||||||
|
return Math.round(d * 10) / 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TPSType {
|
||||||
|
ONE_SECOND,
|
||||||
|
TEN_SECONDS,
|
||||||
|
ONE_MINUTE,
|
||||||
|
FIVE_MINUTES,
|
||||||
|
TEN_MINUTES
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public class VersionDependent {
|
||||||
|
private VersionDependent() {}
|
||||||
|
|
||||||
|
public static <T> T getVersionImpl(Plugin plugin) {
|
||||||
|
return getVersionImpl(plugin, (new Exception()).getStackTrace()[1].getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getVersionImpl(Plugin plugin, String className){
|
||||||
|
ClassLoader loader = plugin.getClass().getClassLoader();
|
||||||
|
for(int version = Core.getVersion(); version >= 8; version--) {
|
||||||
|
try {
|
||||||
|
return ((Class<? extends T>) Class.forName(className + version, true, loader)).getDeclaredConstructor().newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||||
|
throw new SecurityException("Could not load version dependent class", e);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// try next version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new SecurityException("Unable to find version dependent implementation for " + className);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package de.steamwar.core;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class WorldEditWrapper {
|
||||||
|
private WorldEditWrapper() {}
|
||||||
|
|
||||||
|
public static final IWorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
|
public interface IWorldEditWrapper {
|
||||||
|
InputStream getPlayerClipboard(Player player, boolean schemFormat);
|
||||||
|
void setPlayerClipboard(Player player, InputStream is, boolean schemFormat);
|
||||||
|
Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldEditPlugin getWorldEditPlugin() {
|
||||||
|
return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.authlib;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfileRepository;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
|
||||||
|
|
||||||
|
public class AuthlibInjector {
|
||||||
|
private AuthlibInjector() {}
|
||||||
|
|
||||||
|
public static void inject() {
|
||||||
|
Class<?> minecraftServerClass = Reflection.getClass("{nms.server}.MinecraftServer");
|
||||||
|
Reflection.FieldAccessor<GameProfileRepository> gameProfile = Reflection.getField(minecraftServerClass, GameProfileRepository.class, 0);
|
||||||
|
Object minecraftServer = Reflection.getTypedMethod(minecraftServerClass, "getServer", minecraftServerClass).invoke(null);
|
||||||
|
gameProfile.set(minecraftServer, new SteamwarGameProfileRepository((YggdrasilGameProfileRepository) gameProfile.get(minecraftServer)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.authlib;
|
||||||
|
|
||||||
|
import com.mojang.authlib.Agent;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.GameProfileRepository;
|
||||||
|
import com.mojang.authlib.ProfileLookupCallback;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SteamwarGameProfileRepository implements GameProfileRepository {
|
||||||
|
|
||||||
|
private final YggdrasilGameProfileRepository fallback;
|
||||||
|
|
||||||
|
public SteamwarGameProfileRepository(YggdrasilGameProfileRepository repository) {
|
||||||
|
fallback = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findProfilesByNames(String[] strings, Agent agent, ProfileLookupCallback profileLookupCallback) {
|
||||||
|
if(agent == Agent.SCROLLS) {
|
||||||
|
fallback.findProfilesByNames(strings, agent, profileLookupCallback);
|
||||||
|
} else {
|
||||||
|
List<String> unknownNames = new ArrayList<>();
|
||||||
|
for (String name:strings) {
|
||||||
|
SteamwarUser user = SteamwarUser.get(name);
|
||||||
|
if(user == null) {
|
||||||
|
unknownNames.add(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileLookupCallback.onProfileLookupSucceeded(new GameProfile(user.getUUID(), user.getUserName()));
|
||||||
|
}
|
||||||
|
if(!unknownNames.isEmpty()) {
|
||||||
|
fallback.findProfilesByNames(unknownNames.toArray(new String[0]), agent, profileLookupCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.events;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.SWException;
|
||||||
|
import de.steamwar.techhider.ProtocolUtils;
|
||||||
|
import de.steamwar.techhider.TechHider;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class AntiNocom implements Listener {
|
||||||
|
|
||||||
|
private final Map<Player, Integer> flags = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public AntiNocom() {
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(this, Core.getInstance());
|
||||||
|
TinyProtocol.instance.addFilter(blockDig, this::onDig);
|
||||||
|
|
||||||
|
if(Core.getVersion() > 8) {
|
||||||
|
registerUseItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onQuit(PlayerQuitEvent e) {
|
||||||
|
flags.remove(e.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerUseItem() {
|
||||||
|
Class<?> useItem = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseItem");
|
||||||
|
|
||||||
|
Function<Object, Object> getPosition;
|
||||||
|
if(Core.getVersion() > 12) {
|
||||||
|
Class<?> movingObjectPositionBlock = Reflection.getClass("{nms.world.phys}.MovingObjectPositionBlock");
|
||||||
|
Reflection.FieldAccessor<?> useItemPosition = Reflection.getField(useItem, movingObjectPositionBlock, 0);
|
||||||
|
Reflection.FieldAccessor<?> movingBlockPosition = Reflection.getField(movingObjectPositionBlock, TechHider.blockPosition, 0);
|
||||||
|
|
||||||
|
getPosition = (packet) -> movingBlockPosition.get(useItemPosition.get(packet));
|
||||||
|
} else {
|
||||||
|
getPosition = Reflection.getField(useItem, TechHider.blockPosition, 0)::get;
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyProtocol.instance.addFilter(useItem, (player, packet) -> {
|
||||||
|
Object pos = getPosition.apply(packet);
|
||||||
|
return isValid(player, "UseItem", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> blockDig = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInBlockDig");
|
||||||
|
private static final Reflection.FieldAccessor<?> digPosition = Reflection.getField(blockDig, TechHider.blockPosition, 0);
|
||||||
|
private Object onDig(Player player, Object packet) {
|
||||||
|
Object pos = digPosition.get(packet);
|
||||||
|
return isValid(player, "Dig", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValid(Player player, String type, int x, int z) {
|
||||||
|
if((x == 0 && z == 0) || player.getWorld().isChunkLoaded(ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int amount = flags.compute(player, (p, a) -> a == null ? 1 : a+1);
|
||||||
|
if(amount % 8 == 0) { // Only after 8 and every 8 to reduce false flags and spam
|
||||||
|
if(amount == 8) // Schedule player kick only once
|
||||||
|
Bukkit.getScheduler().runTask(Core.getInstance(), () -> player.kickPlayer(null));
|
||||||
|
|
||||||
|
SWException.log(player.getName() + " kicked for potential NoCom-DOS attack", x + " " + z + " " + type + " " + amount);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.events;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
|
|
||||||
|
public class ChattingEvent implements Listener {
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
private void onChat(AsyncPlayerChatEvent event) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
Core.MESSAGE.broadcastPrefixless("LOCAL_CHAT", event.getPlayer().getDisplayName(), event.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.events;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import com.viaversion.viaversion.api.Via;
|
||||||
|
import com.viaversion.viaversion.api.ViaAPI;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.core.CraftbukkitWrapper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TinyProtocol can't translate BlockEntities during 1.16 to 1.17 conversions du to removed partial chunk update support. This class cancels PartialChunkUpdates for this players and sends them a complete chunk instead.
|
||||||
|
* This class can only be loaded on 1.9 to 1.15 with active ViaVersion.
|
||||||
|
**/
|
||||||
|
public class PartialChunkFixer {
|
||||||
|
|
||||||
|
private static final int PROTOCOL1_17 = 755;
|
||||||
|
private static final Class<?> mapChunk = Reflection.getClass("{nms}.PacketPlayOutMapChunk");
|
||||||
|
private static final Reflection.FieldAccessor<Boolean> fullChunkFlag = Reflection.getField(mapChunk, boolean.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkX = Reflection.getField(mapChunk, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> chunkZ = Reflection.getField(mapChunk, int.class, 1);
|
||||||
|
|
||||||
|
private final ViaAPI<Player> via = Via.getAPI();
|
||||||
|
|
||||||
|
private final List<ResendChunk> chunksToResend = new ArrayList<>();
|
||||||
|
|
||||||
|
public PartialChunkFixer() {
|
||||||
|
TinyProtocol.instance.addFilter(mapChunk, this::chunkFilter);
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
|
||||||
|
synchronized (chunksToResend) {
|
||||||
|
for(ResendChunk chunk : chunksToResend) {
|
||||||
|
CraftbukkitWrapper.impl.sendChunk(chunk.player, chunk.x, chunk.z);
|
||||||
|
}
|
||||||
|
chunksToResend.clear();
|
||||||
|
}
|
||||||
|
}, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object chunkFilter(Player player, Object packet) {
|
||||||
|
if(via.getPlayerVersion(player) >= PROTOCOL1_17 && !fullChunkFlag.get(packet)) {
|
||||||
|
// partial chunk update
|
||||||
|
synchronized (chunksToResend) {
|
||||||
|
chunksToResend.add(new ResendChunk(player, chunkX.get(packet), chunkZ.get(packet)));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ResendChunk {
|
||||||
|
private final Player player;
|
||||||
|
private final int x;
|
||||||
|
private final int z;
|
||||||
|
|
||||||
|
private ResendChunk(Player player, int x, int z) {
|
||||||
|
this.player = player;
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.events;
|
||||||
|
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import de.steamwar.sql.UserPerm;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
|
||||||
|
public class PlayerJoinedEvent implements Listener{
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
private void onJoin(PlayerJoinEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
SteamwarUser user = Statement.productionDatabase() ? SteamwarUser.get(player.getUniqueId()) : SteamwarUser.getOrCreate(player.getUniqueId(), player.getName(), uuid -> {}, (oldName, newName) -> {});
|
||||||
|
|
||||||
|
UserPerm.Prefix prefix = user.prefix();
|
||||||
|
if(prefix != UserPerm.emptyPrefix) {
|
||||||
|
player.setDisplayName(prefix.getColorCode() + prefix.getChatPrefix() + " " + player.getName() + "§r");
|
||||||
|
} else
|
||||||
|
player.setDisplayName(prefix.getColorCode() + player.getName() + "§r");
|
||||||
|
|
||||||
|
event.setJoinMessage("§a§l» §r" + player.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private void onQuit(PlayerQuitEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
event.setQuitMessage("§c§l« §r" + player.getDisplayName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core.events;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
|
|
||||||
|
public class WorldLoadEvent implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onWorldInit(WorldInitEvent e){
|
||||||
|
if(Integer.parseInt(System.getProperty("fightID", "0")) != -1) // On testarenas not
|
||||||
|
e.getWorld().setKeepSpawnInMemory(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.entity;
|
||||||
|
|
||||||
|
import de.steamwar.core.BountifulWrapper;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class RArmorStand extends REntity {
|
||||||
|
|
||||||
|
private static int sizeIndex() {
|
||||||
|
switch(Core.getVersion()) {
|
||||||
|
case 8:
|
||||||
|
case 9:
|
||||||
|
return 10;
|
||||||
|
case 10:
|
||||||
|
case 12:
|
||||||
|
return 11;
|
||||||
|
case 14:
|
||||||
|
return 13;
|
||||||
|
case 15:
|
||||||
|
return 14;
|
||||||
|
case 18:
|
||||||
|
case 19:
|
||||||
|
default:
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(sizeIndex(), Byte.class);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Size size;
|
||||||
|
|
||||||
|
public RArmorStand(REntityServer server, Location location, Size size) {
|
||||||
|
super(server, EntityType.ARMOR_STAND, location, 0);
|
||||||
|
this.size = size;
|
||||||
|
server.addEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void spawn(Consumer<Object> packetSink) {
|
||||||
|
super.spawn(packetSink);
|
||||||
|
|
||||||
|
if(size != null && size != Size.NORMAL)
|
||||||
|
packetSink.accept(getDataWatcherPacket(sizeWatcher, size.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Size {
|
||||||
|
NORMAL((byte) 0x00),
|
||||||
|
SMALL((byte) 0x01),
|
||||||
|
MARKER((byte) 0x10);
|
||||||
|
|
||||||
|
private final byte value;
|
||||||
|
|
||||||
|
Size(byte value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.entity;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import de.steamwar.core.*;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class REntity {
|
||||||
|
|
||||||
|
private static final Object entityStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(0, Byte.class);
|
||||||
|
private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(FlatteningWrapper.EntityPose.NORMAL).getClass());
|
||||||
|
private static final Object bowDrawnWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 7 : 6, Byte.class);
|
||||||
|
private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Core.getVersion() > 12 ? Optional.class : String.class); // Optional<IChatBaseComponent>
|
||||||
|
private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class);
|
||||||
|
|
||||||
|
private static final Object noGravityDataWatcher = BountifulWrapper.impl.getDataWatcherObject(5,Boolean.class);
|
||||||
|
|
||||||
|
private static int entityIdCounter = -1;
|
||||||
|
private static final Random random = new Random();
|
||||||
|
|
||||||
|
private final REntityServer server;
|
||||||
|
private final EntityType entityType;
|
||||||
|
@Getter
|
||||||
|
protected final int entityId;
|
||||||
|
@Getter
|
||||||
|
protected final UUID uuid;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected double x;
|
||||||
|
@Getter
|
||||||
|
protected double y;
|
||||||
|
@Getter
|
||||||
|
protected double z;
|
||||||
|
private byte yaw;
|
||||||
|
private byte pitch;
|
||||||
|
private byte headYaw;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private boolean hidden;
|
||||||
|
@Getter
|
||||||
|
private boolean invisible;
|
||||||
|
@Getter
|
||||||
|
private FlatteningWrapper.EntityPose pose = FlatteningWrapper.EntityPose.NORMAL;
|
||||||
|
@Getter
|
||||||
|
private boolean bowDrawn;
|
||||||
|
@Getter
|
||||||
|
private boolean noGravity;
|
||||||
|
private boolean isGlowing;
|
||||||
|
private int fireTick;
|
||||||
|
|
||||||
|
private final int objectData;
|
||||||
|
@Getter
|
||||||
|
private String displayName;
|
||||||
|
protected final Map<Object, ItemStack> itemSlots;
|
||||||
|
|
||||||
|
public REntity(REntityServer server, EntityType entityType, Location location) {
|
||||||
|
this(server,entityType,location,0);
|
||||||
|
server.addEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected REntity(REntityServer server, EntityType entityType, Location location,int objectData) {
|
||||||
|
this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), location,objectData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected REntity(REntityServer server, EntityType entityType, UUID uuid, Location location,int objectData) {
|
||||||
|
this.server = server;
|
||||||
|
this.entityType = entityType;
|
||||||
|
this.entityId = entityIdCounter--;
|
||||||
|
this.uuid = uuid;
|
||||||
|
|
||||||
|
this.x = location.getX();
|
||||||
|
this.y = location.getY();
|
||||||
|
this.z = location.getZ();
|
||||||
|
this.headYaw = this.yaw = rotToByte(location.getYaw());
|
||||||
|
this.pitch = rotToByte(location.getPitch());
|
||||||
|
|
||||||
|
this.itemSlots = entityType == EntityType.PLAYER ? new HashMap<>() : null;
|
||||||
|
|
||||||
|
this.noGravity = false;
|
||||||
|
this.isGlowing = false;
|
||||||
|
|
||||||
|
this.objectData = objectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide(boolean hide) {
|
||||||
|
if(hidden == hide)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(hide) {
|
||||||
|
despawn(packet -> server.updateEntity(this, packet));
|
||||||
|
hidden = true;
|
||||||
|
} else {
|
||||||
|
hidden = false;
|
||||||
|
spawn(packet -> server.updateEntity(this, packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(Location location) {
|
||||||
|
move(location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), rotToByte(location.getYaw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final double MAX_REL_MOVE = Core.getVersion() > 8 ? 8.0 : 4.0;
|
||||||
|
public void move(double locX, double locY, double locZ, float pitch, float yaw, byte headYaw) {
|
||||||
|
server.preEntityMove(this, locX, locZ);
|
||||||
|
|
||||||
|
double fromX = this.x;
|
||||||
|
double fromZ = this.z;
|
||||||
|
double diffX = locX - x;
|
||||||
|
double diffY = locY - y;
|
||||||
|
double diffZ = locZ - z;
|
||||||
|
boolean rotEq = this.yaw == yaw && this.pitch == pitch;
|
||||||
|
|
||||||
|
this.x = locX;
|
||||||
|
this.y = locY;
|
||||||
|
this.z = locZ;
|
||||||
|
this.yaw = rotToByte(yaw);
|
||||||
|
this.pitch = rotToByte(pitch);
|
||||||
|
|
||||||
|
if(Math.abs(diffX) < MAX_REL_MOVE && Math.abs(diffY) < MAX_REL_MOVE && Math.abs(diffZ) < MAX_REL_MOVE) {
|
||||||
|
Object packet = getMoveLookPacket(diffX, diffY, diffZ,rotEq);
|
||||||
|
|
||||||
|
if(packet != null)
|
||||||
|
server.updateEntity(this, packet);
|
||||||
|
} else {
|
||||||
|
server.updateEntity(this, getTeleportPacket());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.headYaw != headYaw) {
|
||||||
|
this.headYaw = headYaw;
|
||||||
|
server.updateEntity(this, getHeadRotationPacket());
|
||||||
|
}
|
||||||
|
|
||||||
|
server.postEntityMove(this, fromX, fromZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> animationPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutAnimation");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> animationEntity = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? (Core.getVersion() > 19 ? 5 : 6) : 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> animationAnimation = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? (Core.getVersion() > 19 ? 6 : 7) : 1);
|
||||||
|
public void showAnimation(byte animation) {
|
||||||
|
Object packet = Reflection.newInstance(animationPacket);
|
||||||
|
animationEntity.set(packet, entityId);
|
||||||
|
animationAnimation.set(packet, (int) animation);
|
||||||
|
server.updateEntity(this, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> velocityPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityVelocity");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> velocityEntity = Reflection.getField(velocityPacket, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> velocityX = Reflection.getField(velocityPacket, int.class, 1);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> velocityY = Reflection.getField(velocityPacket, int.class, 2);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> velocityZ = Reflection.getField(velocityPacket, int.class, 3);
|
||||||
|
public void setVelocity(double dX, double dY, double dZ) {
|
||||||
|
Object packet = Reflection.newInstance(velocityPacket);
|
||||||
|
velocityEntity.set(packet, entityId);
|
||||||
|
velocityX.set(packet, calcVelocity(dX));
|
||||||
|
velocityY.set(packet, calcVelocity(dY));
|
||||||
|
velocityZ.set(packet, calcVelocity(dZ));
|
||||||
|
server.updateEntity(this, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> statusPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityStatus");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> statusEntity = Reflection.getField(statusPacket, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> statusStatus = Reflection.getField(statusPacket, byte.class, 0);
|
||||||
|
public void showDamage() {
|
||||||
|
Object packet = Reflection.newInstance(statusPacket);
|
||||||
|
statusEntity.set(packet, entityId);
|
||||||
|
statusStatus.set(packet, (byte) 2);
|
||||||
|
server.updateEntity(this, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPose(FlatteningWrapper.EntityPose pose) {
|
||||||
|
this.pose = pose;
|
||||||
|
if(Core.getVersion() > 12) {
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
|
||||||
|
} else {
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnFire(boolean perma) {
|
||||||
|
fireTick = perma ? -1 : 21;
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnFire() {
|
||||||
|
return fireTick == -1 || fireTick > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvisible(boolean invisible) {
|
||||||
|
this.invisible = invisible;
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBowDrawn(boolean drawn, boolean offHand) {
|
||||||
|
bowDrawn = drawn;
|
||||||
|
if(Core.getVersion() > 8){
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(bowDrawnWatcher, (byte) ((drawn ? 1 : 0) + (offHand ? 2 : 0))));
|
||||||
|
}else{
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayName(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(
|
||||||
|
nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName),
|
||||||
|
nameVisibleWatcher, displayName != null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Object slot, ItemStack stack) {
|
||||||
|
itemSlots.put(slot, stack);
|
||||||
|
server.updateEntity(this, getEquipmentPacket(slot, stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void die() {
|
||||||
|
server.removeEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoGravity(boolean noGravity) {
|
||||||
|
this.noGravity = noGravity;
|
||||||
|
if(Core.getVersion() > 8)
|
||||||
|
server.updateEntity(this,getDataWatcherPacket(noGravityDataWatcher,noGravity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGlowing(boolean glowing) {
|
||||||
|
this.isGlowing = glowing;
|
||||||
|
if(Core.getVersion() > 8) {
|
||||||
|
server.updateEntity(this,getDataWatcherPacket(entityStatusWatcher,getEntityStatus()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGlowing() {
|
||||||
|
return isGlowing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int spawnPacketOffset() {
|
||||||
|
switch (Core.getVersion()) {
|
||||||
|
case 8:
|
||||||
|
case 18:
|
||||||
|
return 1;
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
case 12:
|
||||||
|
case 14:
|
||||||
|
case 15:
|
||||||
|
return 0;
|
||||||
|
case 19:
|
||||||
|
default:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static final Function<REntity, Object> spawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnPacket, spawnPacketOffset());
|
||||||
|
private static int objectDataOffset() {
|
||||||
|
switch (Core.getVersion()) {
|
||||||
|
case 8:
|
||||||
|
return 9;
|
||||||
|
case 9:
|
||||||
|
case 14:
|
||||||
|
case 12:
|
||||||
|
case 10:
|
||||||
|
case 15:
|
||||||
|
case 18:
|
||||||
|
return 6;
|
||||||
|
case 19:
|
||||||
|
default:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static final Reflection.FieldAccessor<Integer> additionalData = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, objectDataOffset());
|
||||||
|
private Object spawnPacketGenerator() {
|
||||||
|
Object packet = spawnPacketGenerator.apply(this);
|
||||||
|
additionalData.set(packet, objectData);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void list(Consumer<Object> packetSink) {
|
||||||
|
// empty for regular entity
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Function<REntity, Object> livingSpawnPacketGenerator = Core.getVersion() >= 19 ? REntity::spawnPacketGenerator : entitySpawnPacketGenerator(ProtocolWrapper.spawnLivingPacket, Core.getVersion() == 8 ? 2 : 0);
|
||||||
|
void spawn(Consumer<Object> packetSink) {
|
||||||
|
if(entityType.isAlive()) {
|
||||||
|
packetSink.accept(livingSpawnPacketGenerator.apply(this));
|
||||||
|
} else {
|
||||||
|
packetSink.accept(spawnPacketGenerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
postSpawn(packetSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void postSpawn(Consumer<Object> packetSink) {
|
||||||
|
if(headYaw != 0) {
|
||||||
|
packetSink.accept(getHeadRotationPacket());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Core.getVersion() > 12 && pose != FlatteningWrapper.EntityPose.NORMAL) {
|
||||||
|
packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte status = getEntityStatus();
|
||||||
|
if(status != 0) {
|
||||||
|
packetSink.accept(getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(displayName != null) {
|
||||||
|
packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName), nameVisibleWatcher, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Core.getVersion() > 8 && noGravity)
|
||||||
|
packetSink.accept(getDataWatcherPacket(noGravityDataWatcher, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() {
|
||||||
|
if(fireTick > 0) {
|
||||||
|
fireTick--;
|
||||||
|
if(fireTick == 0) {
|
||||||
|
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> destroyPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityDestroy");
|
||||||
|
private static final Reflection.FieldAccessor<?> destroyEntities;
|
||||||
|
static {
|
||||||
|
if(Core.getVersion() > 15)
|
||||||
|
destroyEntities = Reflection.getField(destroyPacket, IntList.class, 0);
|
||||||
|
else
|
||||||
|
destroyEntities = Reflection.getField(destroyPacket, int[].class, 0);
|
||||||
|
}
|
||||||
|
void despawn(Consumer<Object> packetSink){
|
||||||
|
Object packet = Reflection.newInstance(destroyPacket);
|
||||||
|
destroyEntities.set(packet, Core.getVersion() > 15 ? new IntArrayList(new int[]{entityId}) : new int[]{entityId});
|
||||||
|
packetSink.accept(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delist(Consumer<Object> packetSink) {
|
||||||
|
// empty for regular entity
|
||||||
|
}
|
||||||
|
|
||||||
|
double x() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
double z() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte getEntityStatus() {
|
||||||
|
byte status = 0;
|
||||||
|
|
||||||
|
if(fireTick != 0)
|
||||||
|
status |= 1;
|
||||||
|
if(pose == FlatteningWrapper.EntityPose.SNEAKING)
|
||||||
|
status |= 2;
|
||||||
|
if(Core.getVersion() == 8 && bowDrawn)
|
||||||
|
status |= 0x10;
|
||||||
|
if(invisible)
|
||||||
|
status |= 0x20;
|
||||||
|
if(Core.getVersion() > 8 && isGlowing)
|
||||||
|
status |= 0x40;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getDataWatcherPacket(Object... dataWatcherKeyValues) {
|
||||||
|
return ChatWrapper.impl.getDataWatcherPacket(entityId, dataWatcherKeyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> teleportEntity = Reflection.getField(teleportPacket, int.class, 0);
|
||||||
|
private static final BountifulWrapper.PositionSetter teleportPosition = BountifulWrapper.impl.getPositionSetter(teleportPacket, Core.getVersion() == 8 ? 1 : 0);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> teleportYaw = Reflection.getField(teleportPacket, byte.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> teleportPitch = Reflection.getField(teleportPacket, byte.class, 1);
|
||||||
|
private Object getTeleportPacket(){
|
||||||
|
Object packet = Reflection.newInstance(teleportPacket);
|
||||||
|
teleportEntity.set(packet, entityId);
|
||||||
|
teleportPosition.set(packet, x, y, z);
|
||||||
|
teleportYaw.set(packet, yaw);
|
||||||
|
teleportPitch.set(packet, pitch);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> entityPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntity");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> moveEntityId = Reflection.getField(entityPacket, int.class, 0);
|
||||||
|
private static final BountifulWrapper.PositionSetter movePosition = BountifulWrapper.impl.getRelMoveSetter(entityPacket);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> lookYaw = Reflection.getField(entityPacket, "e", byte.class);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> lookPitch = Reflection.getField(entityPacket, "f", byte.class);
|
||||||
|
private static final Class<?> lookPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntity$PacketPlayOutEntityLook");
|
||||||
|
private static final Class<?> movePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntity$PacketPlayOutRelEntityMove");
|
||||||
|
private static final Class<?> moveLookPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook");
|
||||||
|
private Object getMoveLookPacket(double diffX, double diffY, double diffZ, boolean rotEq) {
|
||||||
|
Class<?> clazz;
|
||||||
|
if(diffX == 0 && diffY == 0 && diffZ == 0) {
|
||||||
|
if(rotEq)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
clazz = lookPacket;
|
||||||
|
} else if (rotEq) {
|
||||||
|
clazz = movePacket;
|
||||||
|
} else {
|
||||||
|
clazz = moveLookPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object packet = Reflection.newInstance(clazz);
|
||||||
|
moveEntityId.set(packet, entityId);
|
||||||
|
movePosition.set(packet, diffX, diffY, diffZ);
|
||||||
|
lookYaw.set(packet, yaw);
|
||||||
|
lookPitch.set(packet, pitch);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> headRotationPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityHeadRotation");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> headRotationEntity = Reflection.getField(headRotationPacket, int.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Byte> headRotationYaw = Reflection.getField(headRotationPacket, byte.class, 0);
|
||||||
|
private Object getHeadRotationPacket(){
|
||||||
|
Object packet = Reflection.newInstance(headRotationPacket);
|
||||||
|
headRotationEntity.set(packet, entityId);
|
||||||
|
headRotationYaw.set(packet, headYaw);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<Integer> equipmentEntity = Reflection.getField(ProtocolWrapper.equipmentPacket, int.class, 0);
|
||||||
|
|
||||||
|
private static final Class<?> craftItemStack = Reflection.getClass("{obc}.inventory.CraftItemStack");
|
||||||
|
private static final Reflection.MethodInvoker asNMSCopy = Reflection.getTypedMethod(REntity.craftItemStack, "asNMSCopy", ProtocolWrapper.itemStack, ItemStack.class);
|
||||||
|
protected Object getEquipmentPacket(Object slot, ItemStack stack){
|
||||||
|
Object packet = Reflection.newInstance(ProtocolWrapper.equipmentPacket);
|
||||||
|
equipmentEntity.set(packet, entityId);
|
||||||
|
ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack));
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<REntity, Object> entitySpawnPacketGenerator(Class<?> spawnPacket, int posOffset) {
|
||||||
|
BountifulWrapper.UUIDSetter uuid = BountifulWrapper.impl.getUUIDSetter(spawnPacket);
|
||||||
|
Function<REntity, Object> packetGenerator = spawnPacketGenerator(spawnPacket, posOffset);
|
||||||
|
|
||||||
|
return entity -> {
|
||||||
|
Object packet = packetGenerator.apply(entity);
|
||||||
|
uuid.set(packet, entity.uuid);
|
||||||
|
FlatteningWrapper.impl.setSpawnPacketType(packet, entity.entityType);
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Function<REntity, Object> spawnPacketGenerator(Class<?> spawnPacket, int posOffset) {
|
||||||
|
Reflection.FieldAccessor<Integer> entityId = Reflection.getField(spawnPacket, int.class, 0);
|
||||||
|
BountifulWrapper.PositionSetter position = BountifulWrapper.impl.getPositionSetter(spawnPacket, posOffset);
|
||||||
|
|
||||||
|
return entity -> {
|
||||||
|
Object packet = Reflection.newInstance(spawnPacket);
|
||||||
|
entityId.set(packet, entity.entityId);
|
||||||
|
position.set(packet, entity.x, entity.y, entity.z);
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte rotToByte(float rot) {
|
||||||
|
return (byte)((int)(rot * 256.0F / 360.0F));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calcVelocity(double value) {
|
||||||
|
return (int)(Math.max(-3.9, Math.min(value, 3.9)) * 8000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.entity;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.core.FlatteningWrapper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
public class REntityServer implements Listener {
|
||||||
|
|
||||||
|
private static final HashSet<REntity> emptyEntities = new HashSet<>(0);
|
||||||
|
private static final HashSet<Player> emptyPlayers = new HashSet<>(0);
|
||||||
|
|
||||||
|
private static final Class<?> useEntity = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseEntity");
|
||||||
|
private static final Reflection.FieldAccessor<Integer> useEntityTarget = Reflection.getField(useEntity, int.class, 0);
|
||||||
|
private static final Class<?> useEntityEnumAction = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseEntity$EnumEntityUseAction");
|
||||||
|
private static final Reflection.FieldAccessor<?> useEntityAction = Reflection.getField(useEntity, useEntityEnumAction, 0);
|
||||||
|
private static final Function<Object, Integer> getEntityAction;
|
||||||
|
static {
|
||||||
|
if(Core.getVersion() > 15) {
|
||||||
|
Reflection.MethodInvoker useEntityGetAction = Reflection.getMethod(useEntityEnumAction, "a");
|
||||||
|
getEntityAction = value -> ((Enum<?>) useEntityGetAction.invoke(value)).ordinal();
|
||||||
|
} else {
|
||||||
|
getEntityAction = value -> ((Enum<?>) value).ordinal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Integer, REntity> entityMap = new ConcurrentHashMap<>();
|
||||||
|
private final HashMap<Long, HashSet<REntity>> entities = new HashMap<>();
|
||||||
|
private final HashMap<Long, Set<Player>> players = new HashMap<>();
|
||||||
|
private final HashMap<Player, Location> lastLocation = new HashMap<>();
|
||||||
|
private final HashMap<Player, Integer> viewDistance = new HashMap<>();
|
||||||
|
|
||||||
|
private EntityActionListener callback = null;
|
||||||
|
private final Set<Player> playersThatClicked = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
private final BiFunction<Player, Object, Object> filter = (player, packet) -> {
|
||||||
|
REntity entity = entityMap.get(useEntityTarget.get(packet));
|
||||||
|
if (entity == null)
|
||||||
|
return packet;
|
||||||
|
|
||||||
|
if (playersThatClicked.contains(player))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
playersThatClicked.add(player);
|
||||||
|
EntityAction action = getEntityAction.apply(useEntityAction.get(packet)) == 1 ? EntityAction.ATTACK : EntityAction.INTERACT;
|
||||||
|
Bukkit.getScheduler().runTask(Core.getInstance(), () -> {
|
||||||
|
playersThatClicked.remove(player);
|
||||||
|
callback.onAction(player, entity, action);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
public REntityServer() {
|
||||||
|
Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(EntityActionListener callback) {
|
||||||
|
boolean uninitialized = this.callback == null;
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
if(uninitialized)
|
||||||
|
TinyProtocol.instance.addFilter(useEntity, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
Location location = player.getLocation();
|
||||||
|
lastLocation.put(player, location);
|
||||||
|
viewDistance.put(player, viewRadius(player));
|
||||||
|
forChunkInView(player, location, (x, z) -> addPlayerToChunk(player, x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
if (!viewDistance.containsKey(player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forChunkInView(player, lastLocation.remove(player), (x, z) -> removePlayerFromChunk(player, x, z));
|
||||||
|
viewDistance.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Player> getPlayers() {
|
||||||
|
return new ArrayList<>(viewDistance.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
TinyProtocol.instance.removeFilter(useEntity, filter);
|
||||||
|
for(Player player : lastLocation.keySet().toArray(new Player[0])) {
|
||||||
|
removePlayer(player);
|
||||||
|
}
|
||||||
|
HandlerList.unregisterAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEntity(REntity entity) {
|
||||||
|
entityMap.put(entity.entityId, entity);
|
||||||
|
addEntityToChunk(entity);
|
||||||
|
entity.list(packet -> updateEntity(entity, packet));
|
||||||
|
entity.spawn(packet -> updateEntity(entity, packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
void preEntityMove(REntity entity, double toX, double toZ) {
|
||||||
|
long fromId = entityToId(entity);
|
||||||
|
long toId = posToId(toX, toZ);
|
||||||
|
if(fromId == toId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!entity.isHidden())
|
||||||
|
onMissing(players.get(fromId), players.get(toId), entity::despawn);
|
||||||
|
onMissing(players.get(fromId), players.get(toId), entity::delist);
|
||||||
|
removeEntityFromChunk(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postEntityMove(REntity entity, double fromX, double fromZ) {
|
||||||
|
long fromId = posToId(fromX, fromZ);
|
||||||
|
long toId = entityToId(entity);
|
||||||
|
if(fromId == toId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
addEntityToChunk(entity);
|
||||||
|
onMissing(players.get(toId), players.get(fromId), entity::list);
|
||||||
|
if(!entity.isHidden())
|
||||||
|
onMissing(players.get(toId), players.get(fromId), entity::spawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEntity(REntity entity, Object packet) {
|
||||||
|
if(entity.isHidden())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(Player player : players.getOrDefault(entityToId(entity), emptyPlayers)) {
|
||||||
|
TinyProtocol.instance.sendPacket(player, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEntity(REntity entity) {
|
||||||
|
entity.despawn(packet -> updateEntity(entity, packet));
|
||||||
|
removeEntityFromChunk(entity);
|
||||||
|
entity.delist(packet -> updateEntity(entity, packet));
|
||||||
|
entityMap.remove(entity.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<REntity> getEntities() {
|
||||||
|
return new ArrayList<>(entityMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends REntity> List<T> getEntitiesByType(Class<T> clazz) {
|
||||||
|
return entityMap.values().stream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEntityToChunk(REntity entity) {
|
||||||
|
entities.computeIfAbsent(entityToId(entity), i -> new HashSet<>()).add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeEntityFromChunk(REntity entity) {
|
||||||
|
long id = entityToId(entity);
|
||||||
|
HashSet<REntity> entitiesInChunk = entities.get(id);
|
||||||
|
entitiesInChunk.remove(entity);
|
||||||
|
if(entitiesInChunk.isEmpty())
|
||||||
|
entities.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
|
public void onMove(PlayerMoveEvent e) {
|
||||||
|
Player player = e.getPlayer();
|
||||||
|
Location from = lastLocation.get(player);
|
||||||
|
Location to = e.getTo();
|
||||||
|
if(from == null || to == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int fromX = posToChunk(from.getX());
|
||||||
|
int fromZ = posToChunk(from.getZ());
|
||||||
|
int toX = posToChunk(to.getX());
|
||||||
|
int toZ = posToChunk(to.getZ());
|
||||||
|
if(fromX == toX && fromZ == toZ)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastLocation.put(player, to);
|
||||||
|
|
||||||
|
int toViewDistance = viewRadius(player);
|
||||||
|
forChunkInView(player, from, (x, z) -> {
|
||||||
|
if(Math.abs(x - toX) > toViewDistance || Math.abs(z - toZ) > toViewDistance) {
|
||||||
|
removePlayerFromChunk(player, x, z);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
int fromViewDistance = this.viewDistance.put(player, toViewDistance);
|
||||||
|
forChunkInView(player, to, (x, z) -> {
|
||||||
|
if(Math.abs(x - fromX) > fromViewDistance || Math.abs(z - fromZ) > fromViewDistance) {
|
||||||
|
addPlayerToChunk(player, x, z);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onQuit(PlayerQuitEvent e) {
|
||||||
|
Player player = e.getPlayer();
|
||||||
|
Location location = lastLocation.remove(player);
|
||||||
|
if(location == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
forChunkInView(player, location, (x, z) -> {
|
||||||
|
long id = chunkToId(x, z);
|
||||||
|
Set<Player> playersInChunk = players.get(id);
|
||||||
|
playersInChunk.remove(player);
|
||||||
|
if(playersInChunk.isEmpty())
|
||||||
|
players.remove(id);
|
||||||
|
});
|
||||||
|
viewDistance.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onMissing(Set<Player> of, Set<Player> in, Consumer<Consumer<Object>> packetProvider) {
|
||||||
|
if(of == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(Player player : of) {
|
||||||
|
if(in == null || !in.contains(player)) {
|
||||||
|
packetProvider.accept(packet -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forChunkInView(Player player, Location location, BiConsumer<Integer, Integer> func) {
|
||||||
|
int chunkX = posToChunk(location.getX());
|
||||||
|
int chunkZ = posToChunk(location.getZ());
|
||||||
|
int viewDistance = this.viewDistance.get(player);
|
||||||
|
|
||||||
|
for(int x = chunkX - viewDistance; x <= chunkX + viewDistance; x++) {
|
||||||
|
for(int z = chunkZ - viewDistance; z <= chunkZ + viewDistance; z++) {
|
||||||
|
func.accept(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPlayerToChunk(Player player, int x, int z) {
|
||||||
|
long id = chunkToId(x, z);
|
||||||
|
players.computeIfAbsent(id, i -> new HashSet<>()).add(player);
|
||||||
|
for(REntity entity : entities.getOrDefault(id, emptyEntities)) {
|
||||||
|
entity.list(packet -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
|
if(!entity.isHidden())
|
||||||
|
entity.spawn(packet -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removePlayerFromChunk(Player player, int x, int z) {
|
||||||
|
long id = chunkToId(x, z);
|
||||||
|
|
||||||
|
Set<Player> playersInChunk = players.get(id);
|
||||||
|
playersInChunk.remove(player);
|
||||||
|
if(playersInChunk.isEmpty())
|
||||||
|
players.remove(id);
|
||||||
|
|
||||||
|
for(REntity entity : entities.getOrDefault(id, emptyEntities)) {
|
||||||
|
if(!entity.isHidden())
|
||||||
|
entity.despawn(packet -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
|
entity.delist(packet -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
for(HashSet<REntity> entitiesInChunk : entities.values()) {
|
||||||
|
for(REntity entity : entitiesInChunk) {
|
||||||
|
entity.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int posToChunk(double coord) {
|
||||||
|
return (int)(coord / 16) - (coord < 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int viewRadius(Player player) {
|
||||||
|
return FlatteningWrapper.impl.getViewDistance(player) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long entityToId(REntity entity) {
|
||||||
|
return posToId(entity.x(), entity.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
private long posToId(double x, double z) {
|
||||||
|
return chunkToId(posToChunk(x), posToChunk(z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long chunkToId(int x, int z) {
|
||||||
|
return ((long) x << 32) + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EntityAction {
|
||||||
|
INTERACT,
|
||||||
|
ATTACK,
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EntityActionListener {
|
||||||
|
void onAction(Player player, REntity entity, EntityAction action);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.entity;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.techhider.BlockIds;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class RFallingBlockEntity extends REntity{
|
||||||
|
|
||||||
|
private final Material material;
|
||||||
|
|
||||||
|
public RFallingBlockEntity(REntityServer server, Location location, Material material) {
|
||||||
|
super(server, EntityType.FALLING_BLOCK, location, BlockIds.impl.materialToId(material) >> (Core.getVersion() <= 12 ? 4 : 0));
|
||||||
|
this.material = material;
|
||||||
|
server.addEntity(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.entity;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import de.steamwar.core.BountifulWrapper;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.core.FlatteningWrapper;
|
||||||
|
import de.steamwar.core.ProtocolWrapper;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class RPlayer extends REntity {
|
||||||
|
|
||||||
|
private static int skinPartsIndex() {
|
||||||
|
switch(Core.getVersion()) {
|
||||||
|
case 8:
|
||||||
|
return 10;
|
||||||
|
case 9:
|
||||||
|
return 12;
|
||||||
|
case 10:
|
||||||
|
case 12:
|
||||||
|
return 13;
|
||||||
|
case 14:
|
||||||
|
return 15;
|
||||||
|
case 15:
|
||||||
|
return 16;
|
||||||
|
case 18:
|
||||||
|
case 19:
|
||||||
|
default:
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(skinPartsIndex(), Byte.class);
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public RPlayer(REntityServer server, UUID uuid, String name, Location location) {
|
||||||
|
super(server, EntityType.PLAYER, uuid, location,0);
|
||||||
|
this.name = name;
|
||||||
|
server.addEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void list(Consumer<Object> packetSink) {
|
||||||
|
packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(uuid, name), GameMode.CREATIVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void spawn(Consumer<Object> packetSink) {
|
||||||
|
packetSink.accept(getNamedSpawnPacket());
|
||||||
|
packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F));
|
||||||
|
|
||||||
|
for (Map.Entry<Object, ItemStack> entry : itemSlots.entrySet()) {
|
||||||
|
packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
postSpawn(packetSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void delist(Consumer<Object> packetSink) {
|
||||||
|
packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(uuid, name), GameMode.CREATIVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn");
|
||||||
|
private static final Function<REntity, Object> namedSpawnPacketGenerator = spawnPacketGenerator(namedSpawnPacket, Core.getVersion() == 8 ? 1 : 0);
|
||||||
|
private static final Reflection.FieldAccessor<UUID> namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0);
|
||||||
|
private Object getNamedSpawnPacket() {
|
||||||
|
Object packet = namedSpawnPacketGenerator.apply(this);
|
||||||
|
namedSpawnUUID.set(packet, uuid);
|
||||||
|
FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import org.bukkit.event.inventory.ClickType;
|
||||||
|
|
||||||
|
public interface InvCallback {
|
||||||
|
void clicked(ClickType click);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import net.wesjd.anvilgui.AnvilGUI;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SWAnvilInv {
|
||||||
|
|
||||||
|
private final AnvilGUI.Builder builder;
|
||||||
|
private final Player player;
|
||||||
|
private final String defaultText;
|
||||||
|
|
||||||
|
private Runnable leftCallback;
|
||||||
|
private Consumer<String> callback;
|
||||||
|
|
||||||
|
public SWAnvilInv(Player p, String t) {
|
||||||
|
this(p, t, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWAnvilInv(Player p, String t, String defaultText) {
|
||||||
|
builder = new AnvilGUI.Builder().plugin(Core.getInstance()).title(t).text("»" + defaultText).onClick(this::onResult);
|
||||||
|
this.defaultText = defaultText;
|
||||||
|
player = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Material m) {
|
||||||
|
setItem(m, new ArrayList<>(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Material m, byte meta) {
|
||||||
|
setItem(m, meta, new ArrayList<>(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Material m, List<String> lore, boolean e) {
|
||||||
|
setItem(m, (byte)0, lore, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Material m, byte meta, List<String> lore, boolean e) {
|
||||||
|
builder.itemLeft(new SWItem(m, meta, "»" + defaultText, lore, e, null).getItemStack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(Consumer<String> callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLeftCallback(Runnable callback) {
|
||||||
|
leftCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCloseCallback(Runnable callback) {
|
||||||
|
builder.onClose(p -> callback.run());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
player.setLevel(1);
|
||||||
|
builder.open(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AnvilGUI.ResponseAction> onResult(Integer slot, AnvilGUI.StateSnapshot state) {
|
||||||
|
if(slot != AnvilGUI.Slot.OUTPUT) {
|
||||||
|
if(slot == AnvilGUI.Slot.INPUT_LEFT && leftCallback != null)
|
||||||
|
leftCallback.run();
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = state.getText();
|
||||||
|
if(s.startsWith("»"))
|
||||||
|
s = s.substring(1);
|
||||||
|
callback.accept(s);
|
||||||
|
player.setLevel(0);
|
||||||
|
return Collections.singletonList(AnvilGUI.ResponseAction.close());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.InventoryView;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SWInventory implements Listener {
|
||||||
|
|
||||||
|
final Player player;
|
||||||
|
final Map<Integer, Consumer<InventoryClickEvent>> callbacks = new HashMap<>();
|
||||||
|
final Inventory inventory;
|
||||||
|
String title;
|
||||||
|
boolean open = false;
|
||||||
|
|
||||||
|
public SWInventory(Player p, int size, String t) {
|
||||||
|
player = p;
|
||||||
|
inventory = Bukkit.createInventory(p, size, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWInventory(Player p, int size, String t, Map<Integer, SWItem> items) {
|
||||||
|
this(p, size, t);
|
||||||
|
items.forEach(this::setItem);
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWInventory(Player p, Supplier<Inventory> inventoryConstructor) {
|
||||||
|
player = p;
|
||||||
|
try {
|
||||||
|
inventory = inventoryConstructor.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException("Could not construct inventory", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCloseCallback(InvCallback c) {
|
||||||
|
callbacks.put(-1, inventoryClickEvent -> c.clicked(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCloseRunnable(Runnable c) {
|
||||||
|
callbacks.put(-1, inventoryClickEvent -> c.run());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, ItemStack itemStack, InvCallback c) {
|
||||||
|
inventory.setItem(pos, itemStack);
|
||||||
|
if(c != null) {
|
||||||
|
callbacks.put(pos, inventoryClickEvent -> c.clicked(inventoryClickEvent.getClick()));
|
||||||
|
} else {
|
||||||
|
callbacks.remove(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, SWItem item) {
|
||||||
|
setItem(pos, item.getItemStack(), item.getCallback());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, Material m, String name, InvCallback c){
|
||||||
|
setItem(pos, m, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, Material m, byte meta, String name, InvCallback c){
|
||||||
|
setItem(pos, m, meta, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, Material m, String name, List<String> lore, boolean e, InvCallback c) {
|
||||||
|
setItem(pos, m, (byte) 0, name, lore, e, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int pos, Material m, byte meta, String name, List<String> lore, boolean e, InvCallback c) {
|
||||||
|
SWItem item = new SWItem(m, meta, name, lore, e, c);
|
||||||
|
setItem(pos, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(int pos, InvCallback c) {
|
||||||
|
callbacks.put(pos, inventoryClickEvent -> c.clicked(inventoryClickEvent == null ? null : inventoryClickEvent.getClick()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemEvent(int pos, ItemStack itemStack, Consumer<InventoryClickEvent> c) {
|
||||||
|
inventory.setItem(pos, itemStack);
|
||||||
|
callbacks.put(pos, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemEvent(int pos, Material m, String name, Consumer<InventoryClickEvent> c) {
|
||||||
|
setItemEvent(pos, m, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemEvent(int pos, Material m, byte meta, String name, Consumer<InventoryClickEvent> c) {
|
||||||
|
setItemEvent(pos, m, meta, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemEvent(int pos, Material m, String name, List<String> lore, boolean e, Consumer<InventoryClickEvent> c) {
|
||||||
|
setItemEvent(pos, m, (byte) 0, name, lore, e, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemEvent(int pos, Material m, byte meta, String name, List<String> lore, boolean e, Consumer<InventoryClickEvent> c) {
|
||||||
|
SWItem item = new SWItem(m, meta, name, lore, e, click -> {
|
||||||
|
});
|
||||||
|
setItem(pos, item);
|
||||||
|
setEventCallback(pos, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventCallback(int pos, Consumer<InventoryClickEvent> c) {
|
||||||
|
callbacks.put(pos, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
InventoryView view = player.openInventory(inventory);
|
||||||
|
title = view.getTitle();
|
||||||
|
Core.getInstance().getLogger().info("[SWINV] Opened " + title + " for " + player.getName());
|
||||||
|
if(!open) {
|
||||||
|
Bukkit.getPluginManager().registerEvents(this, Core.getInstance());
|
||||||
|
open = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClick(InventoryClickEvent e) {
|
||||||
|
if (!player.equals(e.getWhoClicked()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (callbacks.containsKey(e.getRawSlot()) && callbacks.get(e.getRawSlot()) != null) {
|
||||||
|
e.setCancelled(true);
|
||||||
|
Core.getInstance().getLogger().info("[SWINV] " + e.getWhoClicked().getName() + " " + e.getClick().name() + " clicked " + e.getRawSlot() + " on " + (e.getCurrentItem() != null ? e.getCurrentItem().getItemMeta().getDisplayName() : "[EMPTY]") + " in " + e.getView().getTitle());
|
||||||
|
callbacks.get(e.getRawSlot()).accept(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClose(InventoryCloseEvent e){
|
||||||
|
if(!player.equals(e.getPlayer()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
InventoryClickEvent.getHandlerList().unregister(this);
|
||||||
|
InventoryCloseEvent.getHandlerList().unregister(this);
|
||||||
|
Core.getInstance().getLogger().info("[SWINV] " + player.getName() + " closed " + title);
|
||||||
|
if(callbacks.containsKey(-1))
|
||||||
|
callbacks.get(-1).accept(null);
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import de.steamwar.core.FlatteningWrapper;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.inventory.ItemFlag;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SWItem {
|
||||||
|
|
||||||
|
private ItemStack itemStack;
|
||||||
|
private ItemMeta itemMeta;
|
||||||
|
private InvCallback callback;
|
||||||
|
|
||||||
|
public static SWItem getPlayerSkull(OfflinePlayer player){
|
||||||
|
return getPlayerSkull(player.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SWItem getPlayerSkull(String playerName){
|
||||||
|
SWItem p = new SWItem();
|
||||||
|
ItemStack head = FlatteningWrapper.impl.setSkullOwner(playerName);
|
||||||
|
p.setItemStack(head);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Material getMaterial(String material){
|
||||||
|
try{
|
||||||
|
Material m = FlatteningWrapper.impl.getMaterial(material);
|
||||||
|
if(m == null)
|
||||||
|
return Material.BARRIER;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}catch(IllegalArgumentException e){
|
||||||
|
return Material.STONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Material getDye(int colorCode){
|
||||||
|
return FlatteningWrapper.impl.getDye(colorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem() {
|
||||||
|
itemStack = new ItemStack(Material.AIR);
|
||||||
|
itemMeta = itemStack.getItemMeta();
|
||||||
|
hideAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem(Material material, String name){
|
||||||
|
this(material, (byte)0, name, new ArrayList<>(), false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem(Material material, String name, InvCallback c){
|
||||||
|
this(material, (byte)0, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem(Material material, byte meta, String name){
|
||||||
|
this(material, meta, name, new ArrayList<>(), false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem(Material material, byte meta, String name, InvCallback c){
|
||||||
|
this(material, meta, name, new ArrayList<>(), false, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem(Material material, String name, List<String> lore, boolean enchanted, InvCallback c) {
|
||||||
|
this(material, (byte)0, name, lore, enchanted, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public SWItem(Material material, byte meta, String name, List<String> lore, boolean enchanted, InvCallback c) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
itemStack = new ItemStack(material, 1, (short)0, meta);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
itemStack = new ItemStack(material, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemMeta = itemStack.getItemMeta();
|
||||||
|
|
||||||
|
if (itemMeta != null) {
|
||||||
|
hideAttributes();
|
||||||
|
|
||||||
|
itemMeta.setDisplayName(name);
|
||||||
|
if (lore != null && !lore.isEmpty()) itemMeta.setLore(lore);
|
||||||
|
if (enchanted) itemMeta.addEnchant(Enchantment.DURABILITY , 10, true);
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
}
|
||||||
|
callback = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SWItem getItemFromJson(JsonObject itemJson) {
|
||||||
|
SWItem item = null;
|
||||||
|
try {
|
||||||
|
if(itemJson.has("color")) {
|
||||||
|
item = new SWItem(SWItem.getDye(itemJson.get("color").getAsInt()),
|
||||||
|
itemJson.has("color")?itemJson.get("color").getAsByte():0,
|
||||||
|
itemJson.get("title").getAsString());
|
||||||
|
}else {
|
||||||
|
item = new SWItem(SWItem.getMaterial(itemJson.get("material").getAsString()), itemJson.get("title").getAsString());
|
||||||
|
}
|
||||||
|
}catch (IllegalArgumentException e) {
|
||||||
|
item = new SWItem(Material.STONE, itemJson.get("title").getAsString());
|
||||||
|
}
|
||||||
|
if(itemJson.has("skullOwner")) {
|
||||||
|
item = SWItem.getPlayerSkull(itemJson.get("skullOwner").getAsString());
|
||||||
|
item.setName(itemJson.get("title").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(itemJson.has("enchanted"))
|
||||||
|
item.setEnchanted(true);
|
||||||
|
if(itemJson.has("lore")) {
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
JsonArray loreArray = itemJson.getAsJsonArray("lore");
|
||||||
|
loreArray.forEach(jsonElement -> lore.add(jsonElement.getAsString()));
|
||||||
|
item.setLore(lore);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideAttributes() {
|
||||||
|
if (itemMeta == null) return;
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_DESTROYS);
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE);
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_PLACED_ON);
|
||||||
|
itemMeta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack() {
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemStack(ItemStack itemStack) {
|
||||||
|
this.itemStack = itemStack;
|
||||||
|
itemMeta = itemStack.getItemMeta();
|
||||||
|
hideAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemMeta getItemMeta() {
|
||||||
|
return itemMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemMeta(ItemMeta itemMeta) {
|
||||||
|
this.itemMeta = itemMeta;
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
hideAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvCallback getCallback() {
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(InvCallback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
itemMeta.setDisplayName(name);
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLore(List<String> lore) {
|
||||||
|
itemMeta.setLore(lore);
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnchanted(boolean enchanted) {
|
||||||
|
if (enchanted){
|
||||||
|
itemMeta.addEnchant(Enchantment.DURABILITY , 10, true);
|
||||||
|
} else {
|
||||||
|
itemMeta.removeEnchant(Enchantment.DURABILITY);
|
||||||
|
}
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.SchematicNode;
|
||||||
|
import de.steamwar.sql.SchematicType;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.inventory.ClickType;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SWListInv<T> extends SWInventory {
|
||||||
|
|
||||||
|
private final List<SWListEntry<T>> elements;
|
||||||
|
|
||||||
|
private final Map<Integer, SWItem> customItems = new HashMap<>();
|
||||||
|
private final boolean dynamicSize;
|
||||||
|
private ListCallback<T> callback;
|
||||||
|
private int page;
|
||||||
|
private boolean opened = false;
|
||||||
|
|
||||||
|
public SWListInv(Player p, String t, List<SWListEntry<T>> l, ListCallback<T> c){
|
||||||
|
this(p, t, true, l, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWListInv(Player p, String t, boolean dynamicSize, List<SWListEntry<T>> l, ListCallback<T> c){
|
||||||
|
super(p, dynamicSize ? dynamicSize(l.size()) : 54, t);
|
||||||
|
callback = c;
|
||||||
|
elements = l;
|
||||||
|
page = 0;
|
||||||
|
this.dynamicSize = dynamicSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open(){
|
||||||
|
opened = true;
|
||||||
|
inventory.clear();
|
||||||
|
callbacks.keySet().stream().filter(i -> i >= 0).collect(Collectors.toList()).forEach(callbacks::remove);
|
||||||
|
if(!callbacks.containsKey(-999)) {
|
||||||
|
setCallback(-999, (ClickType click) -> player.closeInventory());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeBiggerMax()) {
|
||||||
|
if (page != 0) {
|
||||||
|
setItem(45, SWItem.getDye(10), (byte) 10, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_ACTIVE", player), (ClickType click) -> {
|
||||||
|
page--;
|
||||||
|
open();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setItem(45, SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (page < elements.size() / 45 - (elements.size() % 45 == 0 ? 1 : 0)) {
|
||||||
|
setItem(53, SWItem.getDye(10), (byte) 10, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_ACTIVE", player), (ClickType click) -> {
|
||||||
|
page++;
|
||||||
|
open();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setItem(53, SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (!dynamicSize) {
|
||||||
|
setItem(45, SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
|
||||||
|
});
|
||||||
|
setItem(53, SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipageLimit = elements.size() - page * 45;
|
||||||
|
if (ipageLimit > 45 && sizeBiggerMax()) {
|
||||||
|
ipageLimit = 45;
|
||||||
|
}
|
||||||
|
int i = page * 45;
|
||||||
|
for (int ipage = 0; ipage < ipageLimit; ipage++) {
|
||||||
|
SWItem e = elements.get(i).getItem();
|
||||||
|
|
||||||
|
final int pos = i;
|
||||||
|
setItem(ipage, e);
|
||||||
|
setCallback(ipage, (ClickType click) -> callback.clicked(click, elements.get(pos).getObject()));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, SWItem> customItem : customItems.entrySet()) {
|
||||||
|
setItem(customItem.getKey(), customItem.getValue());
|
||||||
|
}
|
||||||
|
super.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(int pos, SWItem item){
|
||||||
|
super.setItem(pos, item);
|
||||||
|
if(!opened)
|
||||||
|
customItems.put(pos, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(ListCallback<T> c){
|
||||||
|
callback = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SWListEntry<UUID>> createPlayerList(UUID without){
|
||||||
|
List<SWListEntry<UUID>> onlinePlayers = new ArrayList<>();
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers()){
|
||||||
|
if(without != null && player.getUniqueId().equals(without))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
onlinePlayers.add(new SWListEntry<>(SWItem.getPlayerSkull(player), player.getUniqueId()));
|
||||||
|
}
|
||||||
|
return onlinePlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SWListEntry<SchematicNode>> getSchemnodeList(SchematicType type, int steamwarUserId){
|
||||||
|
List<SWListEntry<SchematicNode>> schemList = new ArrayList<>();
|
||||||
|
|
||||||
|
List<SchematicNode> schems;
|
||||||
|
if(type == null)
|
||||||
|
schems = SchematicNode.getAllSchematicsAccessibleByUser(steamwarUserId);
|
||||||
|
else
|
||||||
|
schems = SchematicNode.getAllAccessibleSchematicsOfType(steamwarUserId, type.toDB());
|
||||||
|
|
||||||
|
for(SchematicNode s : schems){
|
||||||
|
Material m;
|
||||||
|
if(s.getItem().isEmpty())
|
||||||
|
m = SWItem.getMaterial("CAULDRON_ITEM");
|
||||||
|
else
|
||||||
|
m = SWItem.getMaterial(s.getItem());
|
||||||
|
SWItem item = new SWItem(m,"§e" + s.getName());
|
||||||
|
item.setEnchanted(s.isDir());
|
||||||
|
schemList.add(new SWListEntry<>(item, s));
|
||||||
|
}
|
||||||
|
return schemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sizeBiggerMax(){
|
||||||
|
return dynamicSize ? elements.size() > 54 : elements.size() > 45;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int dynamicSize(int size){
|
||||||
|
return (size>45) ? 54 : (size + 9-size%9);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ListCallback<T>{
|
||||||
|
void clicked(ClickType click, T element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SWListEntry<T>{
|
||||||
|
final SWItem item;
|
||||||
|
final T object;
|
||||||
|
|
||||||
|
public SWListEntry(SWItem item, T object){
|
||||||
|
this.item = item;
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem getItem(){
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getObject(){
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,656 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.*;
|
||||||
|
import lombok.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SchematicSelector {
|
||||||
|
|
||||||
|
private static final Sorting[] all_sortings = Sorting.values();
|
||||||
|
private static final Map<Player, SelectorFilter[]> filterCache = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Player player;
|
||||||
|
@Getter
|
||||||
|
private SteamwarUser user;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private Consumer<SchematicNode> callback;
|
||||||
|
private final SelectorTarget target;
|
||||||
|
@Getter
|
||||||
|
private SelectorFilter filter;
|
||||||
|
private SchematicSelectorInjectable injectable = SchematicSelectorInjectable.DEFAULT;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private PublicMode publicMode = PublicMode.ALL;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private boolean singleDirOpen;
|
||||||
|
private boolean sdoTrigger = false;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private int depth = 0;
|
||||||
|
private Sorting sorting = Sorting.NAME;
|
||||||
|
private boolean invertSorting = false;
|
||||||
|
@Getter
|
||||||
|
private SchematicNode lastParent;
|
||||||
|
|
||||||
|
public SchematicSelector(Player player, SelectorTarget target, Consumer<SchematicNode> callback) {
|
||||||
|
this.player = player;
|
||||||
|
this.user = SteamwarUser.get(player.getUniqueId());
|
||||||
|
this.target = target;
|
||||||
|
this.callback = callback;
|
||||||
|
this.singleDirOpen = !target.target.dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchematicSelector(Player player, SelectorTarget target, SchematicSelectorInjectable injectable, Consumer<SchematicNode> callback) {
|
||||||
|
this(player, target, callback);
|
||||||
|
this.injectable = injectable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
injectable.onSelectorCreate(this);
|
||||||
|
if(publicMode == PublicMode.PUBLIC_ONLY) {
|
||||||
|
this.user = SteamwarUser.get(0);
|
||||||
|
}
|
||||||
|
openList(null);
|
||||||
|
injectable.onSelectorOpen(this, SchematicSelectorInjectable.OpenFrom.FRESH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reOpen() {
|
||||||
|
openList(lastParent);
|
||||||
|
injectable.onSelectorOpen(this, SchematicSelectorInjectable.OpenFrom.REOPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reOpenDirUp() {
|
||||||
|
depth--;
|
||||||
|
openList(dirUp(lastParent));
|
||||||
|
injectable.onSelectorOpen(this, SchematicSelectorInjectable.OpenFrom.REOPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openList(SchematicNode parent) {
|
||||||
|
lastParent = parent;
|
||||||
|
List<SchematicNode> nodes = applySorting(filter != null?getFilteredSchematics():getSchematicList(parent));
|
||||||
|
|
||||||
|
if(sdoTrigger) {
|
||||||
|
sdoTrigger = false;
|
||||||
|
openList(nodes.get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SWListInv.SWListEntry<SchematicNode>> list = new ArrayList<>();
|
||||||
|
|
||||||
|
if(depth != 0) {
|
||||||
|
list.add(new SWListInv.SWListEntry<>(new SWItem(Material.ARROW, Core.MESSAGE.parse("SCHEM_SELECTOR_BACK", player), clickType -> {}), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SchematicNode node : nodes) {
|
||||||
|
if(node.getName().equals("//copy")) continue;
|
||||||
|
list.add(renderItem(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
SWListInv<SchematicNode> inv = new SWListInv<>(player, MessageFormat.format(injectable.createTitle(player), target.target.getName(player), (filter == null || filter.getName() == null)?(parent == null?"/":parent.generateBreadcrumbs()):filter.getName()), false, list, (clickType, node) -> handleClick(node, parent));
|
||||||
|
if(publicMode == PublicMode.ALL) {
|
||||||
|
if(user.getId() == 0) {
|
||||||
|
inv.setItem(48, Material.BUCKET, Core.MESSAGE.parse("SCHEM_SELECTOR_OWN", player), clickType -> {
|
||||||
|
this.user = SteamwarUser.get(player.getUniqueId());
|
||||||
|
openList(null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
inv.setItem(48, Material.GLASS, Core.MESSAGE.parse("SCHEM_SELECTOR_PUB", player), clickType -> {
|
||||||
|
this.user = SteamwarUser.get(0);
|
||||||
|
openList(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(target.target.dirs) {
|
||||||
|
inv.setItem(49, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_SEL_DIR", player), clickType -> {
|
||||||
|
player.closeInventory();
|
||||||
|
callback.accept(parent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(user.getId() != 0) {
|
||||||
|
inv.setItem(50, Material.CHEST, Core.MESSAGE.parse("SCHEM_SELECTOR_NEW_DIR", player), clickType -> createFolderIn(parent));
|
||||||
|
}
|
||||||
|
inv.setItem(51, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER", player), clickType -> openFilter());
|
||||||
|
inv.setItem(47, sorting.mat, Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING", player), Arrays.asList(
|
||||||
|
Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING_CURRENT", player, sorting.parseName(player)),
|
||||||
|
Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING_DIRECTION", player, Core.MESSAGE.parse(invertSorting?"SCHEM_SELECTOR_SORTING_DSC":"SCHEM_SELECTOR_SORTING_ASC", player))
|
||||||
|
), invertSorting, click -> {
|
||||||
|
if(click.isLeftClick()) {
|
||||||
|
cycleSorting();
|
||||||
|
} else {
|
||||||
|
invertSorting = !invertSorting;
|
||||||
|
}
|
||||||
|
openList(parent);
|
||||||
|
});
|
||||||
|
|
||||||
|
injectable.onListRender(this, inv, parent);
|
||||||
|
inv.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SchematicNode dirUp(SchematicNode parent) {
|
||||||
|
if(parent == null) {
|
||||||
|
// Gracefully handle unexpected Updir in Root Folder
|
||||||
|
depth = 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!singleDirOpen) {
|
||||||
|
if(NodeMember.getNodeMember(parent.getId(), user.getId()) != null) {
|
||||||
|
return NodeMember.getNodeMember(parent.getId(), user.getId()).getParent().map(integer -> SchematicNode.byIdAndUser(user, integer)).orElse(null);
|
||||||
|
} else {
|
||||||
|
return getParent(parent).orElse(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Optional<SchematicNode> currentParent = Optional.of(parent);
|
||||||
|
do {
|
||||||
|
sdoTrigger = false;
|
||||||
|
currentParent = currentParent.flatMap(SchematicSelector::getParent);
|
||||||
|
if(!currentParent.isPresent()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
getSchematicList(currentParent.get());
|
||||||
|
} while (sdoTrigger);
|
||||||
|
return currentParent.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleClick(SchematicNode node, SchematicNode parent) {
|
||||||
|
if(node == null) {
|
||||||
|
depth--;
|
||||||
|
openList(dirUp(parent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(node.isDir()) {
|
||||||
|
if(filter != null && target.target.dirs) {
|
||||||
|
player.closeInventory();
|
||||||
|
callback.accept(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filter = null;
|
||||||
|
depth++;
|
||||||
|
openList(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.closeInventory();
|
||||||
|
callback.accept(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cycleSorting() {
|
||||||
|
int next = sorting.ordinal() + 1;
|
||||||
|
if(next >= all_sortings.length) {
|
||||||
|
next = 0;
|
||||||
|
}
|
||||||
|
sorting = all_sortings[next];
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SchematicNode> applySorting(List<SchematicNode> nodes) {
|
||||||
|
if(sorting == Sorting.NAME && !invertSorting) {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Comparator<SchematicNode> comparator = sorting.comparator;
|
||||||
|
if(invertSorting) {
|
||||||
|
comparator = comparator.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.sort(comparator);
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SWListInv.SWListEntry<SchematicNode> renderItem(SchematicNode node) {
|
||||||
|
Material m = SWItem.getMaterial(node.getItem());
|
||||||
|
|
||||||
|
String name = Core.MESSAGE.parse((filter != null && filter.name == null)?"SCHEM_SELECTOR_ITEM_NAME":"SCHEM_SELECTOR_ITEM_NAME_FILTER", player, node.getName());
|
||||||
|
|
||||||
|
if(filter != null && filter.getName() != null) {
|
||||||
|
name = name.replace(filter.getName(), Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_REPLACE", player, filter.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
SWItem item = new SWItem(m, name, Collections.singletonList(node.isDir() ? (Core.MESSAGE.parse("SCHEM_SELECTOR_DIR", player)) : Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name())), !node.isDir() && !node.getSchemtype().writeable(), click -> {
|
||||||
|
});
|
||||||
|
if(!node.isDir() && node.getRank() > 0) {
|
||||||
|
item.setLore(Arrays.asList(Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name()), Core.MESSAGE.parse("SCHEM_SELECTOR_RANK", player, node.getRank())));
|
||||||
|
}
|
||||||
|
return new SWListInv.SWListEntry<>(item, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLeftCloseAction(SWAnvilInv inv, Runnable runnable) {
|
||||||
|
AtomicBoolean wasLeft = new AtomicBoolean(false);
|
||||||
|
inv.addCloseCallback(() -> {
|
||||||
|
if(injectable.onAnvilInvCloseAction(this) == SchematicSelectorInjectable.AnvilInvCloseAction.REOPEN && !wasLeft.get()) {
|
||||||
|
player.closeInventory();
|
||||||
|
Bukkit.getScheduler().runTaskLater(Core.getInstance(), runnable, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inv.addLeftCallback(() -> {
|
||||||
|
wasLeft.set(true);
|
||||||
|
runnable.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFolderIn(SchematicNode parent) {
|
||||||
|
SWAnvilInv inv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_CREATE_DIR_TITLE", player));
|
||||||
|
inv.setItem(Material.CHEST, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
|
||||||
|
inv.setCallback(s -> {
|
||||||
|
if(!SchematicNode.invalidSchemName(new String[] {s})) {
|
||||||
|
if(injectable.onFolderCreate(this, s)) {
|
||||||
|
SchematicNode.createSchematicDirectory(user.getId(), s, parent==null?0:parent.getId());
|
||||||
|
openList(parent);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.closeInventory();
|
||||||
|
});
|
||||||
|
addLeftCloseAction(inv, this::reOpen);
|
||||||
|
inv.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openFilter() {
|
||||||
|
if (filter == null) {
|
||||||
|
filter = new SelectorFilter(null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
SWInventory inv = new SWInventory(player, 9 * 2, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE", player));
|
||||||
|
InvCallback nameCallback = clickType -> {
|
||||||
|
if(clickType.isRightClick()) {
|
||||||
|
filter = filter.withName(null);
|
||||||
|
openFilter();
|
||||||
|
} else {
|
||||||
|
SWAnvilInv swAnvilInv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_ENTER_NAME", player));
|
||||||
|
swAnvilInv.setItem(Material.NAME_TAG, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
|
||||||
|
swAnvilInv.setCallback(s -> {
|
||||||
|
filter = filter.withName(s);
|
||||||
|
openFilter();
|
||||||
|
});
|
||||||
|
addLeftCloseAction(swAnvilInv, this::openFilter);
|
||||||
|
swAnvilInv.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(filter.getName() == null) {
|
||||||
|
inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), nameCallback);
|
||||||
|
} else {
|
||||||
|
inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME_SEARCH", player, filter.getName())), true, nameCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvCallback ownerCallback = clickType -> {
|
||||||
|
if(clickType.isRightClick()) {
|
||||||
|
filter = filter.withOwner(null);
|
||||||
|
openFilter();
|
||||||
|
} else {
|
||||||
|
SWAnvilInv swAnvilInv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_ENTER_OWNER", player));
|
||||||
|
swAnvilInv.setItem(SWItem.getMaterial("SKULL_ITEM"), (byte) 3, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
|
||||||
|
swAnvilInv.setCallback(s -> {
|
||||||
|
if(SteamwarUser.get(s) != null) {
|
||||||
|
filter = filter.withOwner(SteamwarUser.get(s).getId());
|
||||||
|
}
|
||||||
|
openFilter();
|
||||||
|
});
|
||||||
|
addLeftCloseAction(swAnvilInv, this::openFilter);
|
||||||
|
swAnvilInv.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(filter.getOwner() == null) {
|
||||||
|
inv.setItem(1, SWItem.getMaterial("SKULL_ITEM"), (byte) 3, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player), ownerCallback);
|
||||||
|
} else {
|
||||||
|
SteamwarUser tUser = SteamwarUser.get(filter.getOwner());
|
||||||
|
SWItem item = SWItem.getPlayerSkull(tUser.getUserName());
|
||||||
|
item.setName(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player));
|
||||||
|
item.setEnchanted(true);
|
||||||
|
item.setLore(Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER_SEARCH", player, tUser.getUserName())));
|
||||||
|
item.setCallback(ownerCallback);
|
||||||
|
inv.setItem(1, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target.target != Target.SCHEMATIC_TYPE) {
|
||||||
|
InvCallback schemTypeCallback = clickType -> {
|
||||||
|
if(clickType.isRightClick()) {
|
||||||
|
filter = filter.withType(null);
|
||||||
|
openFilter();
|
||||||
|
} else {
|
||||||
|
List<SWListInv.SWListEntry<SchematicType>> types = new ArrayList<>();
|
||||||
|
SchematicType.values().forEach(schematicType -> types.add(new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial(schematicType.getMaterial()), "§e" + schematicType.name(), Collections.emptyList(), schematicType.fightType(), n -> {}), schematicType)));
|
||||||
|
SWListInv<SchematicType> listInv = new SWListInv<>(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_SEL_TYPE", player), types, (clickType1, schematicType) -> {
|
||||||
|
filter = filter.withType(schematicType);
|
||||||
|
openFilter();
|
||||||
|
});
|
||||||
|
listInv.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(filter.getType() == null) {
|
||||||
|
inv.setItem(2, SWItem.getMaterial(SchematicType.Normal.getMaterial()), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), schemTypeCallback);
|
||||||
|
} else {
|
||||||
|
inv.setItem(2, SWItem.getMaterial(filter.getType().getMaterial()), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE_SEARCH", player, filter.getType().name())), true, schemTypeCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InvCallback materialCallback = clickType -> {
|
||||||
|
if(clickType.isRightClick()) {
|
||||||
|
filter = filter.withItem(null);
|
||||||
|
openFilter();
|
||||||
|
} else {
|
||||||
|
UtilGui.openMaterialSelector(player, material -> {
|
||||||
|
filter = filter.withItem(material);
|
||||||
|
openFilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final int iSlot = target.target == Target.SCHEMATIC_TYPE?2:3;
|
||||||
|
|
||||||
|
if(filter.getItem() == null) {
|
||||||
|
inv.setItem(iSlot, Material.STONE, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), materialCallback);
|
||||||
|
} else {
|
||||||
|
inv.setItem(iSlot, filter.getItem(), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT_SEARCH", player, filter.getItem().name())), true, materialCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filterCache.containsKey(player)) {
|
||||||
|
filterCache.put(player, new SelectorFilter[9]);
|
||||||
|
|
||||||
|
String cfg = UserConfig.getConfig(user.getId(), "selector:filters");
|
||||||
|
|
||||||
|
if (cfg != null) {
|
||||||
|
JsonArray array = JsonParser.parseString(cfg).getAsJsonArray();
|
||||||
|
for (int i = 0; i < array.size(); i++) {
|
||||||
|
JsonObject object = array.get(i).getAsJsonObject();
|
||||||
|
filterCache.get(player)[i] = new SelectorFilter(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectorFilter[] filters = filterCache.get(player);
|
||||||
|
|
||||||
|
for (int i = 0; i < filters.length; i++) {
|
||||||
|
SelectorFilter filterCached = filters[i];
|
||||||
|
if (filterCached == null) {
|
||||||
|
inv.setItem(i + 9, new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_EMPTY", player), click -> {}));
|
||||||
|
} else {
|
||||||
|
SWItem item = filterCached.toItemStack(player);
|
||||||
|
item.setEnchanted(filterCached.equals(filter));
|
||||||
|
inv.setItem(i + 9, item.getItemStack(), click -> {
|
||||||
|
filter = filterCached;
|
||||||
|
openFilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inv.setItem(7, SWItem.getDye(1), Core.MESSAGE.parse("SCHEM_SELECTOR_CANCEL", player), clickType -> {
|
||||||
|
filter = null;
|
||||||
|
depth = 0;
|
||||||
|
openList(null);
|
||||||
|
});
|
||||||
|
inv.setItem(8, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_GO", player), clickType -> {
|
||||||
|
if (!filter.equals(filters[0])) {
|
||||||
|
for (int i = filters.length - 1; i > 0; i--) {
|
||||||
|
filters[i] = filters[i-1];
|
||||||
|
if (filter.equals(filters[i])) {
|
||||||
|
filters[i] = null;
|
||||||
|
for (int j = i; j < filters.length - 1; j++) {
|
||||||
|
filters[j] = filters[j+1];
|
||||||
|
}
|
||||||
|
filters[filters.length - 1] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filters[0] = filter;
|
||||||
|
|
||||||
|
filterCache.put(player, filters);
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
for (SelectorFilter f : filters) {
|
||||||
|
if (f != null) {
|
||||||
|
array.add(f.toJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserConfig.updatePlayerConfig(user.getId(), "selector:filters", array.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
injectable.onFilterApply(this);
|
||||||
|
depth = 0;
|
||||||
|
openList(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
injectable.onFilterRender(this, inv);
|
||||||
|
|
||||||
|
inv.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SchematicNode> getFilteredSchematics() {
|
||||||
|
List<SchematicNode> nodes = new ArrayList<>(SchematicNode.getAll(user));
|
||||||
|
nodes.removeIf(node -> {
|
||||||
|
injectable.onNodeFilter(this, node);
|
||||||
|
return !filter.matches(node);
|
||||||
|
});
|
||||||
|
if(target.target == Target.DIRECTORY) {
|
||||||
|
nodes.removeIf(node -> !node.isDir());
|
||||||
|
}
|
||||||
|
if(target.target == Target.SCHEMATIC_TYPE) {
|
||||||
|
nodes.removeIf(node -> node.isDir() || !node.getSchemtype().equals(target.type));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SchematicNode> getSchematicList(SchematicNode parent) {
|
||||||
|
List<SchematicNode> nodes = new ArrayList<>();
|
||||||
|
switch (target.target) {
|
||||||
|
case DIRECTORY:
|
||||||
|
nodes.addAll(SchematicNode.list(user, Optional.ofNullable(parent).map(SchematicNode::getId).orElse(null)));
|
||||||
|
nodes.removeIf(node -> !node.isDir());
|
||||||
|
break;
|
||||||
|
case SCHEMATIC_TYPE:
|
||||||
|
nodes.addAll(SchematicNode.accessibleByUserTypeParent(user, target.type, parent==null?null:parent.getId()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
nodes.addAll(SchematicNode.list(user, parent == null?null:parent.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(singleDirOpen && nodes.size() == 1 && nodes.get(0).isDir()) {
|
||||||
|
sdoTrigger = true;
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<SchematicNode> getParent(SchematicNode node) {
|
||||||
|
return node.getOptionalParent().map(integer -> SchematicNode.byIdAndUser(SteamwarUser.get(node.getEffectiveOwner()), integer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectorTarget selectSchematic() {
|
||||||
|
return new SelectorTarget(Target.SCHEMATIC, null, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectorTarget selectDirectory() {
|
||||||
|
return new SelectorTarget(Target.DIRECTORY, null, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectorTarget selectSchematicNode() {
|
||||||
|
return new SelectorTarget(Target.SCHEMATIC_NODE, null, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectorTarget selectSchematicType(SchematicType type) {
|
||||||
|
return new SelectorTarget(Target.SCHEMATIC_TYPE, type, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectorTarget selectSchematicTypeWithRank(SchematicType type, int rank) {
|
||||||
|
return new SelectorTarget(Target.SCHEMATIC_TYPE, type, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class SelectorTarget {
|
||||||
|
|
||||||
|
private final Target target;
|
||||||
|
private final SchematicType type;
|
||||||
|
@Deprecated
|
||||||
|
private final int rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private enum Target {
|
||||||
|
SCHEMATIC("SCHEM_SELECTOR_SCHEMATIC", false),
|
||||||
|
DIRECTORY("SCHEM_SELECTOR_DIRECTORY", true),
|
||||||
|
SCHEMATIC_NODE("SCHEM_SELECTOR_SCHEMATIC_NODE", true),
|
||||||
|
SCHEMATIC_TYPE("SCHEM_SELECTOR_SCHEMATIC", false);
|
||||||
|
|
||||||
|
private final String rawName;
|
||||||
|
private final boolean dirs;
|
||||||
|
|
||||||
|
private String getName(Player player) {
|
||||||
|
return Core.MESSAGE.parse(rawName, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@With
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public static class SelectorFilter {
|
||||||
|
private final String name;
|
||||||
|
private final Integer owner;
|
||||||
|
private final SchematicType type;
|
||||||
|
private final Material item;
|
||||||
|
|
||||||
|
public SelectorFilter(JsonObject object) {
|
||||||
|
this.name = object.get("name").isJsonNull() ? null : object.get("name").getAsString();
|
||||||
|
this.owner = object.get("owner").isJsonNull() ? null : object.get("owner").getAsInt();
|
||||||
|
this.type = object.get("type").isJsonNull() ? null : SchematicType.fromDB(object.get("type").getAsString());
|
||||||
|
this.item = object.get("item").isJsonNull() ? null : Material.valueOf(object.get("item").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(SchematicNode node) {
|
||||||
|
boolean matches = name == null || node.getName().contains(name);
|
||||||
|
|
||||||
|
if(owner != null && node.getOwner() != owner) {
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type != null && (node.isDir() || !node.getSchemtype().equals(type))) {
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item != null) {
|
||||||
|
String i;
|
||||||
|
if(node.getItem().isEmpty()) {
|
||||||
|
i = node.isDir()?"CHEST":"CAULDRON";
|
||||||
|
} else {
|
||||||
|
i = node.getItem();
|
||||||
|
}
|
||||||
|
if(!item.name().equals(i)) {
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
|
return Arrays.stream(new Object[]{name, owner, type, item}).filter(Objects::nonNull).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getItemLore(Player player) {
|
||||||
|
List<String> lore = new ArrayList<>(4);
|
||||||
|
if(name != null) {
|
||||||
|
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME_SEARCH", player, name));
|
||||||
|
}
|
||||||
|
if(owner != null) {
|
||||||
|
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER_SEARCH", player, SteamwarUser.get(owner).getUserName()));
|
||||||
|
}
|
||||||
|
if(type != null) {
|
||||||
|
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE_SEARCH", player, type.name()));
|
||||||
|
}
|
||||||
|
if(item != null) {
|
||||||
|
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT_SEARCH", player, item.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SWItem toItemStack(Player player) {
|
||||||
|
long count = getCount();
|
||||||
|
String itemName = count != 1 ? Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE_MULTI", player, count) : Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE_SINGLE", player);
|
||||||
|
|
||||||
|
List<String> lore = getItemLore(player);
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
return new SWItem(Material.NAME_TAG, itemName, lore, false, click -> {});
|
||||||
|
} else if (owner != null) {
|
||||||
|
SWItem playerSkull = SWItem.getPlayerSkull(SteamwarUser.get(owner).getUserName());
|
||||||
|
playerSkull.setName(itemName);
|
||||||
|
playerSkull.setLore(lore);
|
||||||
|
return playerSkull;
|
||||||
|
} else if (type != null) {
|
||||||
|
return new SWItem(SWItem.getMaterial(type.getMaterial()), itemName, lore, false, n -> {});
|
||||||
|
} else if (item != null) {
|
||||||
|
return new SWItem(item, itemName, lore, false, click -> {});
|
||||||
|
} else {
|
||||||
|
return new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE_EMPTY", player), Collections.emptyList(), false, click -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject toJson() {
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
object.addProperty("name", name);
|
||||||
|
object.addProperty("owner", owner);
|
||||||
|
object.addProperty("type", type == null?null:type.toDB());
|
||||||
|
object.addProperty("item", item == null?null:item.name());
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PublicMode {
|
||||||
|
ALL,
|
||||||
|
PRIVATE_ONLY,
|
||||||
|
PUBLIC_ONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private enum Sorting {
|
||||||
|
NAME(Material.PAPER, "SCHEM_SELECTOR_SORTING_NAME", Comparator.comparing(SchematicNode::getName)),
|
||||||
|
TYPE(Material.CAULDRON, "SCHEM_SELECTOR_SORTING_TYPE", (o1, o2) -> {
|
||||||
|
if(o1.isDir() || o2.isDir()) {
|
||||||
|
return Boolean.compare(o1.isDir(), o2.isDir());
|
||||||
|
} else {
|
||||||
|
return o1.getSchemtype().name().compareTo(o2.getSchemtype().name());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
LAST_UPDATED(SWItem.getMaterial("WATCH"), "SCHEM_SELECTOR_SORTING_UPDATE", Comparator.comparing(SchematicNode::getLastUpdate));
|
||||||
|
|
||||||
|
private final Material mat;
|
||||||
|
private final String name;
|
||||||
|
private final Comparator<SchematicNode> comparator;
|
||||||
|
|
||||||
|
private String parseName(Player player) {
|
||||||
|
return Core.MESSAGE.parse(name, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.SchematicNode;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface SchematicSelectorInjectable {
|
||||||
|
|
||||||
|
SchematicSelectorInjectable DEFAULT = new SchematicSelectorInjectable() {};
|
||||||
|
|
||||||
|
default String createTitle(Player player) {
|
||||||
|
return Core.MESSAGE.parse("SCHEM_SELECTOR_TITLE", player);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onSelectorCreate(SchematicSelector selector) {}
|
||||||
|
|
||||||
|
default void onListRender(SchematicSelector selector, SWListInv<SchematicNode> inv, SchematicNode parent) {}
|
||||||
|
|
||||||
|
default void onFilterRender(SchematicSelector selector, SWInventory inventory) {}
|
||||||
|
|
||||||
|
default void onFilterApply(SchematicSelector selector) {}
|
||||||
|
|
||||||
|
default boolean onFolderCreate(SchematicSelector selector, String name) {return true;}
|
||||||
|
|
||||||
|
default void onNodeFilter(SchematicSelector selector, SchematicNode node) {}
|
||||||
|
|
||||||
|
default void onSelectorOpen(SchematicSelector selector, OpenFrom from) {}
|
||||||
|
|
||||||
|
default AnvilInvCloseAction onAnvilInvCloseAction(SchematicSelector selector) {return AnvilInvCloseAction.CLOSE;}
|
||||||
|
|
||||||
|
enum OpenFrom {
|
||||||
|
FRESH,
|
||||||
|
REOPEN
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnvilInvCloseAction {
|
||||||
|
CLOSE,
|
||||||
|
REOPEN
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.inventory;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.inventory.SWItem;
|
||||||
|
import de.steamwar.inventory.SWListInv;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class UtilGui {
|
||||||
|
|
||||||
|
public static void openMaterialSelector(Player player, Consumer<Material> callback) {
|
||||||
|
openMaterialSelector(player, Core.MESSAGE.parse("MATERIAL_SELECTOR_TITLE", player), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openMaterialSelector(Player player, String title, Consumer<Material> callback) {
|
||||||
|
List<SWListInv.SWListEntry<Material>> materials = new LinkedList<>();
|
||||||
|
for(Material material : Material.values()){
|
||||||
|
if(material.name().startsWith(Material.LEGACY_PREFIX))
|
||||||
|
continue;
|
||||||
|
SWItem item = new SWItem(material, "§7" + material.name());
|
||||||
|
if(item.getItemMeta() != null && material.isItem()) {
|
||||||
|
materials.add(new SWListInv.SWListEntry<>(item, material));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SWListInv<Material> swListInv = new SWListInv<>(player, title, materials, (clickType3, material) -> callback.accept(material));
|
||||||
|
swListInv.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.message;
|
||||||
|
|
||||||
|
import de.steamwar.core.BountifulWrapper;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class Message {
|
||||||
|
private final String resourceBundleName;
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
public Message(String resourceBundleName, ClassLoader classLoader){
|
||||||
|
this.resourceBundleName = resourceBundleName;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parsing input to a message */
|
||||||
|
|
||||||
|
public TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params){
|
||||||
|
return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, sender, params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parsePrefixed(String message, CommandSender sender, Object... params){
|
||||||
|
return parse(message, true, sender, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parse(String message, CommandSender sender, Object... params){
|
||||||
|
return parse(message, false, sender, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parse(String message, boolean prefixed, CommandSender sender, Object... params){
|
||||||
|
Locale locale;
|
||||||
|
if(sender instanceof Player)
|
||||||
|
locale = getLocale((Player) sender);
|
||||||
|
else
|
||||||
|
locale = Locale.getDefault();
|
||||||
|
|
||||||
|
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleName, locale, classLoader);
|
||||||
|
String pattern = "";
|
||||||
|
if(prefixed)
|
||||||
|
pattern = fromRB(resourceBundle, "PREFIX") + " ";
|
||||||
|
pattern += fromRB(resourceBundle, message);
|
||||||
|
|
||||||
|
return new MessageFormat(pattern, locale).format(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fromRB(ResourceBundle bundle, String key) {
|
||||||
|
String result = bundle.getString(key);
|
||||||
|
if(Core.getVersion() < 12)
|
||||||
|
result = new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Locale getLocale(Player player){
|
||||||
|
return SteamwarUser.get(player.getUniqueId()).getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a message to one player */
|
||||||
|
|
||||||
|
public void send(String message, CommandSender sender, Object... params){
|
||||||
|
send(message, true, sender, ChatMessageType.SYSTEM, null, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPrefixless(String message, CommandSender sender, Object... params){
|
||||||
|
send(message, false, sender, ChatMessageType.SYSTEM, null, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String message, CommandSender sender, ChatMessageType type, Object... params){
|
||||||
|
send(message, true, sender, type, null, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPrefixless(String message, CommandSender sender, ChatMessageType type, Object... params){
|
||||||
|
send(message, false, sender, type, null, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params){
|
||||||
|
send(message, true, sender, ChatMessageType.SYSTEM, onHover, onClick, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPrefixless(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params){
|
||||||
|
send(message, false, sender, ChatMessageType.SYSTEM, onHover, onClick, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String message, boolean prefixed, CommandSender sender, ChatMessageType type, String onHover, ClickEvent onClick, Object... params){
|
||||||
|
TextComponent msg = parseToComponent(message, prefixed, sender, params);
|
||||||
|
if(onHover != null)
|
||||||
|
msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(onHover)));
|
||||||
|
if(onClick != null)
|
||||||
|
msg.setClickEvent(onClick);
|
||||||
|
|
||||||
|
if(sender instanceof Player)
|
||||||
|
BountifulWrapper.impl.sendMessage((Player)sender, type, msg);
|
||||||
|
else
|
||||||
|
sender.sendMessage(msg.toPlainText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send message to all players */
|
||||||
|
|
||||||
|
public void broadcastPrefixless(String message, String onHover, ClickEvent onClick, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
sendPrefixless(message, player, parse(onHover, false, player), onClick, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastPrefixless(String message, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
sendPrefixless(message, player, ChatMessageType.SYSTEM, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastActionbar(String message, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
send(message, player, ChatMessageType.ACTION_BAR, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcast(String message, String onHover, ClickEvent onClick, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
send(message, player, parse(onHover, false, player), onClick, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcast(String message, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
send(message, player, ChatMessageType.SYSTEM, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void chat(String message, Object... params){
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
sendPrefixless(message, player, ChatMessageType.CHAT, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.network;
|
||||||
|
|
||||||
|
import de.steamwar.core.BountifulWrapper;
|
||||||
|
import de.steamwar.network.handlers.InventoryHandler;
|
||||||
|
import de.steamwar.network.packets.PacketHandler;
|
||||||
|
import de.steamwar.network.packets.server.*;
|
||||||
|
import de.steamwar.sql.BauweltMember;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class CoreNetworkHandler extends PacketHandler {
|
||||||
|
|
||||||
|
public CoreNetworkHandler() {
|
||||||
|
super();
|
||||||
|
new InventoryHandler().register();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public void handleBaumemberUpdatePacket(BaumemberUpdatePacket packet) {
|
||||||
|
BauweltMember.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public void handleCloseInventoryPacket(CloseInventoryPacket packet) {
|
||||||
|
Player player = Bukkit.getPlayer(SteamwarUser.get(packet.getPlayerId()).getUUID());
|
||||||
|
if (player != null) {
|
||||||
|
player.closeInventory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public void handleInventoryPacket(InventoryPacket packet) {
|
||||||
|
InventoryHandler.handleInventoryPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public void handlePingPacket(PingPacket packet) {
|
||||||
|
UUID uuid = SteamwarUser.get(packet.getId()).getUUID();
|
||||||
|
if(Bukkit.getPlayer(uuid) != null) {
|
||||||
|
Player player = Bukkit.getPlayer(uuid);
|
||||||
|
BountifulWrapper.impl.playPling(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public void handleLocaleChange(LocaleInvalidationPacket packet) {
|
||||||
|
SteamwarUser.invalidate(packet.getPlayerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.network;
|
||||||
|
|
||||||
|
import de.steamwar.network.packets.NetworkPacket;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||||
|
|
||||||
|
public class NetworkReceiver implements PluginMessageListener {
|
||||||
|
|
||||||
|
public NetworkReceiver() {
|
||||||
|
new CoreNetworkHandler().register();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
|
||||||
|
NetworkPacket.handle(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.network;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.network.packets.NetworkPacket;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class NetworkSender {
|
||||||
|
|
||||||
|
public static void send(NetworkPacket packet) {
|
||||||
|
Bukkit.getOnlinePlayers().stream().findAny().ifPresent(player -> send(packet, player));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void send(NetworkPacket packet, Player player) {
|
||||||
|
player.sendPluginMessage(Core.getInstance(), "sw:bridge", packet.serialize());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.network.handlers;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import de.steamwar.inventory.SWInventory;
|
||||||
|
import de.steamwar.inventory.SWItem;
|
||||||
|
import de.steamwar.network.NetworkSender;
|
||||||
|
import de.steamwar.network.packets.PacketHandler;
|
||||||
|
import de.steamwar.network.packets.client.InventoryCallbackPacket;
|
||||||
|
import de.steamwar.network.packets.server.InventoryPacket;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class InventoryHandler extends PacketHandler {
|
||||||
|
|
||||||
|
@Handler
|
||||||
|
public static void handleInventoryPacket(InventoryPacket packet) {
|
||||||
|
Player player = Bukkit.getPlayer(SteamwarUser.get(packet.getPlayer()).getUUID());
|
||||||
|
Map<Integer, SWItem> items = new HashMap<>();
|
||||||
|
packet.getItems().forEach((i, item) -> {
|
||||||
|
@SuppressWarnings("deprecation") SWItem it = SWItem.getItemFromJson(new JsonParser().parse(item).getAsJsonObject());
|
||||||
|
it.setCallback(click -> NetworkSender.send(InventoryCallbackPacket.builder().owner(packet.getPlayer()).position(i).type(InventoryCallbackPacket.CallbackType.CLICK).clickType(InventoryCallbackPacket.ClickType.getByName(click.name())).build(), player));
|
||||||
|
items.put(i, it);
|
||||||
|
});
|
||||||
|
|
||||||
|
SWInventory inventory = new SWInventory(player, packet.getSize(), packet.getTitle(), items);
|
||||||
|
inventory.addCloseCallback(click -> {
|
||||||
|
if(player.getOpenInventory().getType() != InventoryType.CHEST)
|
||||||
|
NetworkSender.send(InventoryCallbackPacket.builder().owner(packet.getPlayer()).type(InventoryCallbackPacket.CallbackType.CLOSE).build(), player);
|
||||||
|
});
|
||||||
|
inventory.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package de.steamwar.network.handlers;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
|
||||||
|
public class ServerDataHandler {
|
||||||
|
|
||||||
|
public ServerDataHandler() {
|
||||||
|
TinyProtocol.instance.addFilter(Reflection.getClass("{nms.network.protocol.game}.ClientboundServerDataPacket"), (p, o) -> null);
|
||||||
|
TinyProtocol.instance.addFilter(Reflection.getClass("{nms.network.protocol.game}.ServerboundChatSessionUpdatePacket"), (player, packet) -> null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.providers;
|
||||||
|
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class BauServerInfo {
|
||||||
|
private static Integer bauOwner = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
try {
|
||||||
|
bauOwner = SteamwarUser.get(UUID.fromString(Bukkit.getWorlds().get(0).getName())).getId();
|
||||||
|
} catch (IllegalArgumentException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getOwnerId() {
|
||||||
|
return bauOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBauServer() {
|
||||||
|
return bauOwner != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.scoreboard;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.core.FlatteningWrapper;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SWScoreboard {
|
||||||
|
private SWScoreboard() {}
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<String> scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, String.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> scoreboardAction = Reflection.getField(FlatteningWrapper.scoreboardObjective, int.class, Core.getVersion() > 15 ? 3 : 0);
|
||||||
|
private static final Class<?> scoreboardDisplayEnum = Reflection.getClass("{nms.world.scores.criteria}.IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||||
|
private static final Reflection.FieldAccessor<?> scoreboardDisplayType = Reflection.getField(FlatteningWrapper.scoreboardObjective, scoreboardDisplayEnum, 0);
|
||||||
|
private static final Object displayTypeIntegers = scoreboardDisplayEnum.getEnumConstants()[0];
|
||||||
|
|
||||||
|
private static final Reflection.FieldAccessor<String> scoreName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<String> scoreScoreboardName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 1);
|
||||||
|
private static final Reflection.FieldAccessor<Integer> scoreValue = Reflection.getField(FlatteningWrapper.scoreboardScore, int.class, 0);
|
||||||
|
|
||||||
|
private static final HashMap<Player, ScoreboardCallback> playerBoards = new HashMap<>(); //Object -> Scoreboard | Alle Versionen in der Map!
|
||||||
|
private static int toggle = 0; // Scoreboard 0 updates while scoreboard 1 is presenting. toggle marks the current active scoreboard
|
||||||
|
|
||||||
|
private static final String SIDEBAR = "Sidebar";
|
||||||
|
private static final Object[] DELETE_SCOREBOARD = new Object[2];
|
||||||
|
private static final Object[] DISPLAY_SIDEBAR = new Object[2];
|
||||||
|
|
||||||
|
static {
|
||||||
|
Class<?> scoreboardDisplayObjective = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutScoreboardDisplayObjective");
|
||||||
|
Reflection.FieldAccessor<String> scoreboardDisplayName = Reflection.getField(scoreboardDisplayObjective, String.class, 0);
|
||||||
|
Reflection.FieldAccessor<Integer> scoreboardDisplaySlot = Reflection.getField(scoreboardDisplayObjective, int.class, 0);
|
||||||
|
for(int id = 0; id < 2; id++) {
|
||||||
|
DELETE_SCOREBOARD[id] = Reflection.newInstance(FlatteningWrapper.scoreboardObjective);
|
||||||
|
scoreboardName.set(DELETE_SCOREBOARD[id], SIDEBAR + id);
|
||||||
|
scoreboardAction.set(DELETE_SCOREBOARD[id], 1); //1 to remove
|
||||||
|
|
||||||
|
DISPLAY_SIDEBAR[id] = Reflection.newInstance(scoreboardDisplayObjective);
|
||||||
|
scoreboardDisplayName.set(DISPLAY_SIDEBAR[id], SIDEBAR + id);
|
||||||
|
scoreboardDisplaySlot.set(DISPLAY_SIDEBAR[id], 1); // 1 = Sidebar
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
|
||||||
|
toggle ^= 1; // Toggle between 0 and 1
|
||||||
|
|
||||||
|
for(Map.Entry<Player, ScoreboardCallback> scoreboard : playerBoards.entrySet()) {
|
||||||
|
Player player = scoreboard.getKey();
|
||||||
|
ScoreboardCallback callback = scoreboard.getValue();
|
||||||
|
|
||||||
|
TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]);
|
||||||
|
TinyProtocol.instance.sendPacket(player, createSidebarPacket(callback.getTitle()));
|
||||||
|
for(Map.Entry<String, Integer> score : callback.getData().entrySet()){
|
||||||
|
TinyProtocol.instance.sendPacket(player, createScorePacket(score.getKey(), score.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(Core.getInstance(), () -> {
|
||||||
|
if(!player.isOnline())
|
||||||
|
return;
|
||||||
|
TinyProtocol.instance.sendPacket(player, DISPLAY_SIDEBAR[toggle]);
|
||||||
|
}, 2);
|
||||||
|
}
|
||||||
|
}, 10, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean createScoreboard(Player player, ScoreboardCallback callback) {
|
||||||
|
playerBoards.put(player, callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeScoreboard(Player player) {
|
||||||
|
if(playerBoards.remove(player) == null || !player.isOnline())
|
||||||
|
return;
|
||||||
|
|
||||||
|
TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object createSidebarPacket(String name){
|
||||||
|
Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardObjective);
|
||||||
|
scoreboardName.set(packet, SIDEBAR + toggle);
|
||||||
|
scoreboardAction.set(packet, 0); //0 to create
|
||||||
|
FlatteningWrapper.impl.setScoreboardTitle(packet, name);
|
||||||
|
scoreboardDisplayType.set(packet, displayTypeIntegers);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object createScorePacket(String name, int value){
|
||||||
|
Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardScore);
|
||||||
|
scoreName.set(packet, name);
|
||||||
|
scoreScoreboardName.set(packet, SIDEBAR + toggle);
|
||||||
|
scoreValue.set(packet, value);
|
||||||
|
FlatteningWrapper.impl.setScoreAction(packet);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.scoreboard;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public interface ScoreboardCallback {
|
||||||
|
|
||||||
|
HashMap<String, Integer> getData();
|
||||||
|
|
||||||
|
String getTitle();
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PersonalKit {
|
||||||
|
|
||||||
|
private static final Table<PersonalKit> table = new Table<>(PersonalKit.class);
|
||||||
|
private static final SelectStatement<PersonalKit> getKits = table.selectFields("UserID", "GameMode");
|
||||||
|
private static final SelectStatement<PersonalKit> getKit = table.select(Table.PRIMARY);
|
||||||
|
private static final SelectStatement<PersonalKit> getKitInUse = table.selectFields("UserID", "GameMode", "InUse");
|
||||||
|
private static final Statement update = table.insertAll();
|
||||||
|
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int userID;
|
||||||
|
@Getter
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final String gamemode;
|
||||||
|
@Getter
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final String name;
|
||||||
|
@Field
|
||||||
|
private String inventory;
|
||||||
|
@Field
|
||||||
|
private String armor;
|
||||||
|
@Getter
|
||||||
|
@Field(def = "1")
|
||||||
|
private boolean inUse;
|
||||||
|
|
||||||
|
public String getRawInventory() {
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawArmor() {
|
||||||
|
return armor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack[] getInventory(){
|
||||||
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(inventory));
|
||||||
|
return Objects.requireNonNull(config.getList("Inventory")).toArray(new ItemStack[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack[] getArmor(){
|
||||||
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(armor));
|
||||||
|
return Objects.requireNonNull(config.getList("Armor")).toArray(new ItemStack[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInUse() {
|
||||||
|
PersonalKit kit = getKitInUse(userID, gamemode);
|
||||||
|
if(kit != null)
|
||||||
|
kit.setUse(false);
|
||||||
|
setUse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUse(boolean inUse) {
|
||||||
|
this.inUse = inUse;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInventory(ItemStack[] inventory) {
|
||||||
|
this.inventory = saveInvConfig("Inventory", inventory);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArmor(ItemStack[] armor) {
|
||||||
|
this.armor = saveInvConfig("Armor", armor);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContainer(ItemStack[] inventory, ItemStack[] armor) {
|
||||||
|
this.armor = saveInvConfig("Armor", armor);
|
||||||
|
this.inventory = saveInvConfig("Inventory", inventory);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
delete.update(userID, gamemode, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
update.update(userID, gamemode, name, inventory, armor, inUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<PersonalKit> get(int userID, String gamemode){
|
||||||
|
return getKits.listSelect(userID, gamemode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersonalKit get(int userID, String gamemode, String name) {
|
||||||
|
return getKit.select(userID, gamemode, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersonalKit create(int userID, String gamemode, String name, ItemStack[] inventory, ItemStack[] armor){
|
||||||
|
if(armor == null) {
|
||||||
|
armor = new ItemStack[]{null, null, null, null};
|
||||||
|
}
|
||||||
|
PersonalKit kit = new PersonalKit(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor), true);
|
||||||
|
kit.update();
|
||||||
|
return kit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersonalKit getKitInUse(int userID, String gamemode) {
|
||||||
|
return getKitInUse.select(userID, gamemode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String saveInvConfig(String name, ItemStack[] inv) {
|
||||||
|
YamlConfiguration armorConfig = new YamlConfiguration();
|
||||||
|
armorConfig.set(name, inv);
|
||||||
|
|
||||||
|
return armorConfig.saveToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.sql.internal.SQLConfig;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class SQLConfigImpl implements SQLConfig {
|
||||||
|
@Override
|
||||||
|
public Logger getLogger() {
|
||||||
|
return Core.getInstance().getLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maxConnections() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SQLWrapperImpl implements SQLWrapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB) {
|
||||||
|
File folder = new File(Core.getInstance().getDataFolder().getParentFile(), "FightSystem");
|
||||||
|
if(folder.exists()) {
|
||||||
|
for(File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().collect(Collectors.toList())) {
|
||||||
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
if(!config.isConfigurationSection("Schematic"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String type = config.getString("Schematic.Type");
|
||||||
|
assert type != null;
|
||||||
|
String shortcut = config.getString("Schematic.Shortcut");
|
||||||
|
if(tmpFromDB.containsKey(type.toLowerCase()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SchematicType checktype = null;
|
||||||
|
String material = config.getString("Schematic.Material", "STONE_BUTTON");
|
||||||
|
|
||||||
|
if(!config.getStringList("CheckQuestions").isEmpty()) {
|
||||||
|
checktype = new SchematicType("C" + type, "C" + shortcut, SchematicType.Type.CHECK_TYPE, null, material, true);
|
||||||
|
tmpTypes.add(checktype);
|
||||||
|
tmpFromDB.put(checktype.toDB(), checktype);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean manualCheck = config.getBoolean("Schematic.ManualCheck", true);
|
||||||
|
|
||||||
|
SchematicType current = new SchematicType(type, shortcut, config.isConfigurationSection("Server") ? SchematicType.Type.FIGHT_TYPE : SchematicType.Type.NORMAL, checktype, material, manualCheck);
|
||||||
|
tmpTypes.add(current);
|
||||||
|
tmpFromDB.put(type.toLowerCase(), current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String SERVER_VERSION = Bukkit.getServer().getVersion();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void additionalExceptionMetadata(StringBuilder builder) {
|
||||||
|
builder.append("\nPlayers: ");
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers())
|
||||||
|
builder.append(player.getName()).append(" ");
|
||||||
|
builder.append("\nWorlds: ");
|
||||||
|
for(World world : Bukkit.getWorlds())
|
||||||
|
builder.append(world.getName()).append(" ");
|
||||||
|
builder.append("\nServer: ").append(SERVER_VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import de.steamwar.core.WorldEditWrapper;
|
||||||
|
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.sql.Blob;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class SchematicData {
|
||||||
|
|
||||||
|
public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) {
|
||||||
|
try {
|
||||||
|
return WorldEditWrapper.impl.getClipboard(is, schemFormat);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Could not read schem", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final NodeData data;
|
||||||
|
|
||||||
|
public SchematicData(SchematicNode node) {
|
||||||
|
this.data = NodeData.get(node);
|
||||||
|
if(node.isDir())
|
||||||
|
throw new SecurityException("Node is Directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Clipboard load() throws IOException, NoClipboardException {
|
||||||
|
return WorldEditWrapper.impl.getClipboard(data.schemData(), data.getNodeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadToPlayer(Player player) throws IOException, NoClipboardException {
|
||||||
|
WorldEditWrapper.impl.setPlayerClipboard(player, data.schemData(), data.getNodeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveFromPlayer(Player player) throws IOException, NoClipboardException {
|
||||||
|
saveFromPlayer(player, Core.getVersion() > 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException {
|
||||||
|
data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player, newFormat), newFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void saveFromBytes(byte[] bytes, boolean newFormat) {
|
||||||
|
data.saveFromStream(new ByteArrayInputStream(bytes), newFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user