mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-27 02:49:15 +00:00
@@ -16,7 +16,7 @@ dependencies {
|
||||
compileOnly(project(":bukkit:compatibility"))
|
||||
compileOnly(project(":bukkit:legacy"))
|
||||
// Anti Grief
|
||||
compileOnly("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}")
|
||||
compileOnly("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
|
||||
// NBT
|
||||
compileOnly("net.momirealms:sparrow-nbt:${rootProject.properties["sparrow_nbt_version"]}")
|
||||
compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
|
||||
|
||||
@@ -1,24 +1,106 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.worldedit;
|
||||
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.bukkit.BukkitBlockRegistry;
|
||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||
import com.sk89q.worldedit.internal.registry.InputParser;
|
||||
import com.sk89q.worldedit.util.concurrency.LazyReference;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import net.momirealms.craftengine.core.block.AbstractBlockManager;
|
||||
import net.momirealms.craftengine.core.block.BlockStateParser;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WorldEditBlockRegister {
|
||||
private static final Field field$BlockType$blockMaterial;
|
||||
private final Field field$BlockType$blockMaterial;
|
||||
private final AbstractBlockManager manager;
|
||||
private final boolean isFAWE;
|
||||
|
||||
static {
|
||||
public WorldEditBlockRegister(AbstractBlockManager manager, boolean isFAWE) {
|
||||
field$BlockType$blockMaterial = ReflectionUtils.getDeclaredField(BlockType.class, "blockMaterial");
|
||||
this.manager = manager;
|
||||
this.isFAWE = isFAWE;
|
||||
CEBlockParser blockParser = new CEBlockParser(WorldEdit.getInstance());
|
||||
WorldEdit.getInstance().getBlockFactory().register(blockParser);
|
||||
}
|
||||
|
||||
public static void register(Key id) throws ReflectiveOperationException {
|
||||
public void register(Key id) throws ReflectiveOperationException {
|
||||
BlockType blockType = new BlockType(id.toString(), blockState -> blockState);
|
||||
field$BlockType$blockMaterial.set(blockType, LazyReference.from(() -> new BukkitBlockRegistry.BukkitBlockMaterial(null, Material.STONE)));
|
||||
BlockType.REGISTRY.register(id.toString(), blockType);
|
||||
}
|
||||
|
||||
private final class CEBlockParser extends InputParser<BaseBlock> {
|
||||
|
||||
private CEBlockParser(WorldEdit worldEdit) {
|
||||
super(worldEdit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String input) {
|
||||
Set<String> namespacesInUse = manager.namespacesInUse();
|
||||
|
||||
if (input.isEmpty() || input.equals(":")) {
|
||||
return namespacesInUse.stream().map(namespace -> namespace + ":");
|
||||
}
|
||||
|
||||
if (input.startsWith(":")) {
|
||||
String term = input.substring(1);
|
||||
return BlockStateParser.fillSuggestions(term).stream();
|
||||
}
|
||||
|
||||
if (!input.contains(":")) {
|
||||
String lowerSearch = input.toLowerCase();
|
||||
return Stream.concat(
|
||||
namespacesInUse.stream().filter(n -> n.startsWith(lowerSearch)).map(n -> n + ":"),
|
||||
BlockStateParser.fillSuggestions(input).stream()
|
||||
);
|
||||
}
|
||||
return BlockStateParser.fillSuggestions(input).stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock parseFromInput(String input, ParserContext context) {
|
||||
if (isFAWE) {
|
||||
int index = input.indexOf("[");
|
||||
if (input.charAt(index+1) == ']') return null;
|
||||
}
|
||||
|
||||
int colonIndex = input.indexOf(':');
|
||||
if (colonIndex == -1) return null;
|
||||
|
||||
Set<String> namespacesInUse = manager.namespacesInUse();
|
||||
String namespace = input.substring(0, colonIndex);
|
||||
if (!namespacesInUse.contains(namespace)) return null;
|
||||
|
||||
ImmutableBlockState state = BlockStateParser.deserialize(input);
|
||||
if (state == null) return null;
|
||||
|
||||
try {
|
||||
String id = state.customBlockState().handle().toString();
|
||||
int first = id.indexOf('{');
|
||||
int last = id.indexOf('}');
|
||||
if (first != -1 && last != -1 && last > first) {
|
||||
String blockId = id.substring(first + 1, last);
|
||||
BlockType blockType = BlockTypes.get(blockId);
|
||||
if (blockType == null) {
|
||||
return null;
|
||||
}
|
||||
return blockType.getDefaultState().toBaseBlock();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid block ID format: " + id);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ dependencies {
|
||||
implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}")
|
||||
implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}")
|
||||
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
|
||||
implementation("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}")
|
||||
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
|
||||
implementation("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}")
|
||||
}
|
||||
|
||||
@@ -75,5 +75,7 @@ tasks {
|
||||
relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml")
|
||||
relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
|
||||
relocate("net.jpountz", "net.momirealms.craftengine.libraries.jpountz")
|
||||
relocate("software.amazon.awssdk", "net.momirealms.craftengine.libraries.awssdk")
|
||||
relocate("software.amazon.eventstream", "net.momirealms.craftengine.libraries.eventstream")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,20 @@ reload:
|
||||
- /craftengine reload
|
||||
- /ce reload
|
||||
|
||||
upload:
|
||||
enable: true
|
||||
permission: ce.command.admin.upload
|
||||
usage:
|
||||
- /craftengine upload
|
||||
- /ce upload
|
||||
|
||||
send_resource_pack:
|
||||
enable: true
|
||||
permission: ce.command.admin.send_resource_pack
|
||||
usage:
|
||||
- /craftengine feature send-pack
|
||||
- /ce feature send-pack
|
||||
|
||||
get_item:
|
||||
enable: true
|
||||
permission: ce.command.admin.get_item
|
||||
|
||||
@@ -65,29 +65,31 @@ resource-pack:
|
||||
- CustomNameplates/ResourcePack
|
||||
- BetterModel/build
|
||||
- BetterHud/build
|
||||
send:
|
||||
delivery:
|
||||
# Send the resource pack on joining the server
|
||||
send-on-join: true
|
||||
send-on-reload: true
|
||||
kick-if-declined: true
|
||||
prompt: "<yellow>To fully experience our server, please accept our custom resource pack.</yellow>"
|
||||
# If you are hosting the resource pack by yourself, replace `localhost` with your server ip otherwise it would only work on your local pc
|
||||
# If using BungeeCord or Velocity, consider using a proxy-side plugin to handle resource pack delivery.
|
||||
mode: self-host # self-host/external-host/none
|
||||
self-host:
|
||||
ip: localhost
|
||||
port: 8163
|
||||
protocol: http
|
||||
deny-non-minecraft-request: true
|
||||
# If the path begins with `./` or `../`, it is treated as a relative path to the plugin folder.
|
||||
# Otherwise, it is considered an absolute path.
|
||||
local-file-path: "./generated/resource_pack.zip"
|
||||
rate-limit:
|
||||
max-requests: 3
|
||||
reset-interval: 30 # seconds
|
||||
external-host:
|
||||
url: ""
|
||||
sha1: ""
|
||||
uuid: ""
|
||||
# Read this page for more host types: https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host
|
||||
hosting:
|
||||
- type: "self"
|
||||
ip: "localhost"
|
||||
port: 8163
|
||||
protocol: "http"
|
||||
deny-non-minecraft-request: true
|
||||
one-time-token: true
|
||||
rate-limit:
|
||||
max-requests: 3
|
||||
reset-interval: 20
|
||||
# Upload the resource pack automatically on generation
|
||||
# When disabled, you must manually trigger uploads using the /ce upload command
|
||||
auto-upload: true
|
||||
# The file to upload
|
||||
file-to-upload: "./generated/resource_pack.zip"
|
||||
# Resend the resource pack to players upon successful upload
|
||||
resend-on-upload: true
|
||||
duplicated-files-handler:
|
||||
- term:
|
||||
type: any_of
|
||||
|
||||
@@ -28,4 +28,8 @@ adventure-text-serializer-json=${adventure_bundle_version}
|
||||
adventure-text-serializer-json-legacy-impl=${adventure_bundle_version}
|
||||
netty-codec-http=${netty_version}
|
||||
ahocorasick=${ahocorasick_version}
|
||||
lz4=${lz4_version}
|
||||
lz4=${lz4_version}
|
||||
netty-codec-http2=${netty_version}
|
||||
reactive-streams=${reactive_streams_version}
|
||||
amazon-sdk-s3=${amazon_awssdk_version}
|
||||
amazon-sdk-eventstream=${amazon_awssdk_eventstream_version}
|
||||
@@ -1198,133 +1198,133 @@ minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:
|
||||
# Suitable for making some surface decorations and crops.
|
||||
# Tripwire
|
||||
#minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]#
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
#minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]#
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
########################################################################################################################################################################################################################
|
||||
# Can make transparent blocks, but the collision shape is relatively random. Not as useful as leaves.
|
||||
# Chorus Plant
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
items#misc:
|
||||
default:chinese_lantern:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 3000
|
||||
data:
|
||||
item-name: "<!i><i18n:item.chinese_lantern>"
|
||||
@@ -41,7 +41,7 @@ items#misc:
|
||||
"end": "minecraft:block/custom/chinese_lantern_top"
|
||||
"side": "minecraft:block/custom/chinese_lantern"
|
||||
default:netherite_anvil:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 3001
|
||||
data:
|
||||
item-name: "<!i><i18n:item.netherite_anvil>"
|
||||
@@ -113,7 +113,7 @@ items#misc:
|
||||
appearance: axisZ
|
||||
id: 3
|
||||
default:gunpowder_block:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 3002
|
||||
data:
|
||||
item-name: "<!i><i18n:item.gunpowder_block>"
|
||||
@@ -154,7 +154,7 @@ items#misc:
|
||||
textures:
|
||||
"all": "minecraft:block/custom/gunpowder_block"
|
||||
default:solid_gunpowder_block:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 3003
|
||||
data:
|
||||
item-name: "<!i><i18n:item.solid_gunpowder_block>"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# client-bound-data requires CraftEngine mod to apply
|
||||
items:
|
||||
minecraft:string:
|
||||
client-bound-data:
|
||||
components:
|
||||
minecraft:block_state:
|
||||
attached: "false"
|
||||
disarmed: "false"
|
||||
east: "true"
|
||||
north: "true"
|
||||
powered: "true"
|
||||
south: "true"
|
||||
west: "true"
|
||||
minecraft:note_block:
|
||||
client-bound-data:
|
||||
components:
|
||||
minecraft:block_state:
|
||||
instrument: "harp"
|
||||
powered: "false"
|
||||
note: "0"
|
||||
@@ -1,6 +1,6 @@
|
||||
items:
|
||||
default:bench:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 2000
|
||||
data:
|
||||
item-name: "<!i><i18n:item.bench>"
|
||||
@@ -19,7 +19,7 @@ items:
|
||||
ground:
|
||||
rules:
|
||||
# ANY / FOUR / EIGHT / SIXTEEN / NORTH / EAST / WEST / SOUTH
|
||||
rotation: EIGHT
|
||||
rotation: FOUR
|
||||
# ANY / CENTER / HALF / QUARTER / CORNER
|
||||
alignment: CENTER
|
||||
elements:
|
||||
@@ -30,23 +30,20 @@ items:
|
||||
translation: 0,0.5,0
|
||||
hitboxes:
|
||||
- position: 0,0,0
|
||||
width: 1
|
||||
height: 1
|
||||
type: shulker
|
||||
direction: east
|
||||
peek: 100
|
||||
interactive: true
|
||||
interaction-entity: true
|
||||
seats:
|
||||
- 0,0,-0.1 0
|
||||
- position: 1,0,0
|
||||
width: 1
|
||||
height: 1
|
||||
interactive: true
|
||||
seats:
|
||||
- 1,0,-0.1 0
|
||||
loot:
|
||||
template: "default:loot_table/basic"
|
||||
arguments:
|
||||
item: default:bench
|
||||
default:table_lamp:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 2001
|
||||
data:
|
||||
item-name: "<!i><i18n:item.table_lamp>"
|
||||
@@ -90,7 +87,7 @@ items:
|
||||
arguments:
|
||||
item: default:table_lamp
|
||||
default:wooden_chair:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 2002
|
||||
data:
|
||||
item-name: "<!i><i18n:item.wooden_chair>"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
items:
|
||||
default:topaz_ore:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 1010
|
||||
data:
|
||||
item-name: "<!i><i18n:item.topaz_ore>"
|
||||
@@ -13,7 +13,7 @@ items:
|
||||
type: block_item
|
||||
block: default:topaz_ore
|
||||
default:deepslate_topaz_ore:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 1011
|
||||
data:
|
||||
item-name: "<!i><i18n:item.deepslate_topaz_ore>"
|
||||
@@ -26,7 +26,7 @@ items:
|
||||
type: block_item
|
||||
block: default:deepslate_topaz_ore
|
||||
default:topaz:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 1012
|
||||
settings:
|
||||
anvil-repair-item:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
items:
|
||||
default:palm_log:
|
||||
material: paper
|
||||
material: oak_log
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 300
|
||||
@@ -46,8 +46,8 @@ items:
|
||||
from: 0
|
||||
to: 2
|
||||
default:stripped_palm_log:
|
||||
material: paper
|
||||
custom-model-data: 1001
|
||||
material: stripped_oak_log
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 300
|
||||
tags:
|
||||
@@ -89,8 +89,8 @@ items:
|
||||
from: 3
|
||||
to: 5
|
||||
default:palm_wood:
|
||||
material: paper
|
||||
custom-model-data: 1002
|
||||
material: oak_wood
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 300
|
||||
tags:
|
||||
@@ -135,8 +135,8 @@ items:
|
||||
from: 6
|
||||
to: 8
|
||||
default:stripped_palm_wood:
|
||||
material: paper
|
||||
custom-model-data: 1003
|
||||
material: stripped_oak_wood
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 300
|
||||
tags:
|
||||
@@ -178,8 +178,8 @@ items:
|
||||
from: 9
|
||||
to: 11
|
||||
default:palm_planks:
|
||||
material: paper
|
||||
custom-model-data: 1004
|
||||
material: oak_planks
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 300
|
||||
tags:
|
||||
@@ -211,8 +211,8 @@ items:
|
||||
id: 12
|
||||
state: note_block:12
|
||||
default:palm_sapling:
|
||||
material: paper
|
||||
custom-model-data: 1005
|
||||
material: nether_brick
|
||||
custom-model-data: 1000
|
||||
settings:
|
||||
fuel-time: 100
|
||||
data:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
items:
|
||||
default:fairy_flower:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 4000
|
||||
data:
|
||||
item-name: "<!i><i18n:item.fairy_flower>"
|
||||
@@ -12,7 +12,7 @@ items:
|
||||
type: block_item
|
||||
block: default:fairy_flower
|
||||
default:reed:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 4001
|
||||
data:
|
||||
item-name: "<!i><i18n:item.reed>"
|
||||
@@ -24,7 +24,7 @@ items:
|
||||
type: liquid_collision_block_item
|
||||
block: default:reed
|
||||
default:flame_cane:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 4002
|
||||
data:
|
||||
item-name: "<!i><i18n:item.flame_cane>"
|
||||
@@ -36,7 +36,7 @@ items:
|
||||
type: block_item
|
||||
block: default:flame_cane
|
||||
default:ender_pearl_flower_seeds:
|
||||
material: paper
|
||||
material: nether_brick
|
||||
custom-model-data: 4003
|
||||
data:
|
||||
item-name: "<!i><i18n:item.ender_pearl_flower_seeds>"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
author: XiaoMoMi
|
||||
version: 0.0.1
|
||||
description: Remove Shulker Head for Some Versions
|
||||
namespace: minecraft
|
||||
enable: false
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -59,6 +59,10 @@ command.resource.enable.failure.unknown: "<red>Unknown resource <arg:0></red>"
|
||||
command.resource.disable.success: "<white>Disabled resource <arg:0>. Run <click:run_command:/ce reload all><u>/ce reload all</u></click> to apply changes</white>"
|
||||
command.resource.disable.failure.unknown: "<red>Unknown resource <arg:0></red>"
|
||||
command.resource.list: "<white>Enabled resources(<arg:0>): <green><arg:1></green><newline>Disabled resources(<arg:2>): <red><arg:3></red></white>"
|
||||
command.upload.failure.not_supported: "<red>Current hosting method '<arg:0>' doesn't support uploading resource packs.</red>"
|
||||
command.upload.on_progress: "<white>Started uploading progress. Check the console for more information.</white>"
|
||||
command.send_resource_pack.success.single: "<white>Sent resource pack to <arg:0>.</white>"
|
||||
command.send_resource_pack.success.multiple: "<white>Send resource packs to <arg:0> players.</white>"
|
||||
warning.config.image.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'.</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' should be no lower than 'ascent'.</yellow>"
|
||||
|
||||
@@ -59,6 +59,10 @@ command.resource.enable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.disable.success: "<white>已禁用 <arg:0>. 执行 <click:run_command:/ce reload all><u>/ce reload all</u></click> 以应用更改</white>"
|
||||
command.resource.disable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.list: "<white>启用的资源(<arg:0>): <green><arg:1></green><newline>禁用的资源(<arg:2>): <red><arg:3></red></white>"
|
||||
command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支持上传资源包.</red>"
|
||||
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息.</white>"
|
||||
command.send_resource_pack.success.single: "<white>发送资源包给 <arg:0></white>"
|
||||
command.send_resource_pack.success.multiple: "<white>发送资源包给 <arg:0> 个玩家</white>"
|
||||
warning.config.image.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'height' 高度参数</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度</yellow>"
|
||||
|
||||
@@ -87,7 +87,6 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
// Event listeners
|
||||
private final BlockEventListener blockEventListener;
|
||||
private final FallingBlockRemoveListener fallingBlockRemoveListener;
|
||||
private WorldEditCommandHelper weCommandHelper;
|
||||
|
||||
public BukkitBlockManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
@@ -128,18 +127,11 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
if (this.fallingBlockRemoveListener != null) {
|
||||
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, plugin.bootstrap());
|
||||
}
|
||||
boolean hasWE = false;
|
||||
// WorldEdit
|
||||
if (this.plugin.isPluginEnabled("FastAsyncWorldEdit")) {
|
||||
this.initFastAsyncWorldEditHook();
|
||||
hasWE = true;
|
||||
} else if (this.plugin.isPluginEnabled("WorldEdit")) {
|
||||
this.initWorldEditHook();
|
||||
hasWE = true;
|
||||
}
|
||||
if (hasWE) {
|
||||
this.weCommandHelper = new WorldEditCommandHelper(this.plugin, this);
|
||||
this.weCommandHelper.enable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +151,6 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
this.unload();
|
||||
HandlerList.unregisterAll(this.blockEventListener);
|
||||
if (this.fallingBlockRemoveListener != null) HandlerList.unregisterAll(this.fallingBlockRemoveListener);
|
||||
if (this.weCommandHelper != null) this.weCommandHelper.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,13 +172,14 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
}
|
||||
|
||||
public void initFastAsyncWorldEditHook() {
|
||||
// do nothing
|
||||
new WorldEditBlockRegister(this, true);
|
||||
}
|
||||
|
||||
public void initWorldEditHook() {
|
||||
WorldEditBlockRegister weBlockRegister = new WorldEditBlockRegister(this, false);
|
||||
try {
|
||||
for (Key newBlockId : this.blockRegisterOrder) {
|
||||
WorldEditBlockRegister.register(newBlockId);
|
||||
weBlockRegister.register(newBlockId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to initialize world edit hook", e);
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockStateParser;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
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.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO A better command suggestion system
|
||||
public class WorldEditCommandHelper implements Listener {
|
||||
private final BukkitBlockManager manager;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public WorldEditCommandHelper(BukkitCraftEngine plugin, BukkitBlockManager manager) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
String message = event.getMessage();
|
||||
if (!message.startsWith("//")) return;
|
||||
|
||||
Set<String> cachedNamespaces = manager.namespacesInUse();
|
||||
String[] args = message.split(" ");
|
||||
boolean modified = false;
|
||||
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
String[] parts = args[i].split(",");
|
||||
List<String> processedParts = new ArrayList<>(parts.length);
|
||||
boolean partModified = false;
|
||||
|
||||
for (String part : parts) {
|
||||
String processed = processIdentifier(part, cachedNamespaces);
|
||||
partModified |= !part.equals(processed);
|
||||
processedParts.add(processed);
|
||||
}
|
||||
|
||||
if (partModified) {
|
||||
args[i] = String.join(",", processedParts);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
event.setMessage(String.join(" ", args));
|
||||
}
|
||||
}
|
||||
|
||||
private String processIdentifier(String identifier, Set<String> cachedNamespaces) {
|
||||
int colonIndex = identifier.indexOf(':');
|
||||
if (colonIndex == -1) return identifier;
|
||||
|
||||
String namespace = identifier.substring(0, colonIndex);
|
||||
if (!cachedNamespaces.contains(namespace)) return identifier;
|
||||
|
||||
ImmutableBlockState state = BlockStateParser.deserialize(identifier);
|
||||
if (state == null) return identifier;
|
||||
|
||||
try {
|
||||
return BlockStateUtils.getBlockOwnerIdFromState(
|
||||
state.customBlockState().handle()
|
||||
).toString();
|
||||
} catch (NullPointerException e) {
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,19 +96,19 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
return new BushBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
public static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments) {
|
||||
public static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments, boolean aboveOrBelow) {
|
||||
List<Object> mcTags = new ArrayList<>();
|
||||
for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-block-tags", List.of()))) {
|
||||
for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault((aboveOrBelow ? "above" : "bottom") + "-block-tags", List.of()))) {
|
||||
mcTags.add(BlockTags.getOrCreate(Key.of(tag)));
|
||||
}
|
||||
Set<Object> mcBlocks = new HashSet<>();
|
||||
Set<String> customBlocks = new HashSet<>();
|
||||
for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-blocks", List.of()))) {
|
||||
for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault((aboveOrBelow ? "above" : "bottom") + "-blocks", List.of()))) {
|
||||
int index = blockStateStr.indexOf('[');
|
||||
Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr);
|
||||
Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value()));
|
||||
|
||||
@@ -134,7 +134,7 @@ public class CropBlockBehavior extends BushBlockBehavior {
|
||||
Object visualState = immutableBlockState.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, pos, visualState);
|
||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState);
|
||||
if (!is) {
|
||||
sendParticles = true;
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class CropBlockBehavior extends BushBlockBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
Property<Integer> ageProperty = (Property<Integer>) block.getProperty("age");
|
||||
if (ageProperty == null) {
|
||||
throw new IllegalArgumentException("age property not set for crop");
|
||||
|
||||
@@ -31,7 +31,7 @@ public class HangingBlockBehavior extends BushBlockBehavior {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, true);
|
||||
return new HangingBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
Object visualState = immutableBlockState.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, blockPos, visualState);
|
||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
|
||||
if (!is) {
|
||||
sendParticles = true;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
throw new IllegalArgumentException("stage property not set for sapling");
|
||||
}
|
||||
double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45));
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance,
|
||||
MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0)));
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
Property<Integer> ageProperty = (Property<Integer>) block.getProperty("age");
|
||||
if (ageProperty == null) {
|
||||
throw new IllegalArgumentException("age property not set for sugar cane");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.data;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
@@ -98,11 +99,7 @@ public class EntityDataValue {
|
||||
}
|
||||
|
||||
public static Object create(int id, Object serializer, Object value) {
|
||||
try {
|
||||
Object entityDataAccessor = Reflections.constructor$EntityDataAccessor.newInstance(id, serializer);
|
||||
return Reflections.method$SynchedEntityData$DataValue$create.invoke(null, entityDataAccessor, value);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Object entityDataAccessor = FastNMS.INSTANCE.constructor$EntityDataAccessor(id, serializer);
|
||||
return FastNMS.INSTANCE.method$SynchedEntityData$DataValue$create(entityDataAccessor, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
|
||||
import net.momirealms.craftengine.core.entity.furniture.Billboard;
|
||||
@@ -33,17 +34,13 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpawnPackets(int entityId, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<Object> packets) {
|
||||
try {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityId, UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0
|
||||
));
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityId, getCachedValues()));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct element spawn packet", e);
|
||||
}
|
||||
public void initPackets(int entityId, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<Object> packets) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId, UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0
|
||||
));
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues()));
|
||||
}
|
||||
|
||||
private synchronized List<Object> getCachedValues() {
|
||||
|
||||
@@ -143,9 +143,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
elements.add(furnitureElement);
|
||||
}
|
||||
|
||||
// add colliders
|
||||
List<Collider> colliders = new ArrayList<>();
|
||||
|
||||
// external model providers
|
||||
Optional<ExternalModel> externalModel;
|
||||
if (placementArguments.containsKey("model-engine")) {
|
||||
@@ -162,7 +159,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
for (Map<String, Object> config : hitboxConfigs) {
|
||||
HitBox hitBox = HitBoxTypes.fromMap(config);
|
||||
hitboxes.add(hitBox);
|
||||
hitBox.optionalCollider().ifPresent(colliders::add);
|
||||
}
|
||||
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
|
||||
hitboxes.add(InteractionHitBox.DEFAULT);
|
||||
@@ -180,7 +176,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
rotationRule,
|
||||
alignmentRule,
|
||||
externalModel
|
||||
@@ -189,7 +184,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
placements.put(anchorType, new CustomFurniture.Placement(
|
||||
elements.toArray(new FurnitureElement[0]),
|
||||
hitboxes.toArray(new HitBox[0]),
|
||||
colliders.toArray(new Collider[0]),
|
||||
RotationRule.ANY,
|
||||
AlignmentRule.CENTER,
|
||||
externalModel
|
||||
|
||||
@@ -90,10 +90,12 @@ public class LoadedFurniture implements Furniture {
|
||||
|
||||
List<Object> packets = new ArrayList<>();
|
||||
List<Object> minimizedPackets = new ArrayList<>();
|
||||
List<Collider> colliders = new ArrayList<>();
|
||||
|
||||
for (FurnitureElement element : placement.elements()) {
|
||||
int entityId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||
fakeEntityIds.add(entityId);
|
||||
element.addSpawnPackets(entityId, x, y, z, yaw, conjugated, packet -> {
|
||||
element.initPackets(entityId, x, y, z, yaw, conjugated, packet -> {
|
||||
packets.add(packet);
|
||||
if (this.minimized) minimizedPackets.add(packet);
|
||||
});
|
||||
@@ -105,12 +107,12 @@ public class LoadedFurniture implements Furniture {
|
||||
mainEntityIds.add(entityId);
|
||||
this.hitBoxes.put(entityId, hitBox);
|
||||
}
|
||||
hitBox.addSpawnPackets(ids, x, y, z, yaw, conjugated, (packet, canBeMinimized) -> {
|
||||
hitBox.initPacketsAndColliders(ids, x, y, z, yaw, conjugated, (packet, canBeMinimized) -> {
|
||||
packets.add(packet);
|
||||
if (this.minimized && !canBeMinimized) {
|
||||
minimizedPackets.add(packet);
|
||||
}
|
||||
});
|
||||
}, colliders::add);
|
||||
}
|
||||
try {
|
||||
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
@@ -122,13 +124,12 @@ public class LoadedFurniture implements Furniture {
|
||||
}
|
||||
this.fakeEntityIds = fakeEntityIds;
|
||||
this.entityIds = mainEntityIds;
|
||||
int colliderSize = placement.colliders().length;
|
||||
int colliderSize = colliders.size();
|
||||
this.collisionEntities = new CollisionEntity[colliderSize];
|
||||
if (colliderSize != 0) {
|
||||
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
|
||||
for (int i = 0; i < colliderSize; i++) {
|
||||
// TODO better shulker hitbox
|
||||
Collider collider = placement.colliders()[i];
|
||||
Collider collider = colliders.get(i);
|
||||
Vector3f offset = conjugated.transform(new Vector3f(collider.position()));
|
||||
Vector3d offset1 = collider.point1();
|
||||
Vector3d offset2 = collider.point2();
|
||||
|
||||
@@ -10,5 +10,6 @@ public class BukkitHitBoxTypes extends HitBoxTypes {
|
||||
register(INTERACTION, InteractionHitBox.FACTORY);
|
||||
register(SHULKER, ShulkerHitBox.FACTORY);
|
||||
register(HAPPY_GHAST, HappyGhastHitBox.FACTORY);
|
||||
register(CUSTOM, CustomHitBox.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CustomHitBox extends AbstractHitBox {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float scale;
|
||||
private final EntityType entityType;
|
||||
private final List<Object> cachedValues = new ArrayList<>();
|
||||
|
||||
public CustomHitBox(Seat[] seats, Vector3f position, EntityType type, float scale) {
|
||||
super(seats, position);
|
||||
this.scale = scale;
|
||||
this.entityType = type;
|
||||
BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||
BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues);
|
||||
}
|
||||
|
||||
public EntityType entityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public float scale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return HitBoxTypes.CUSTOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPacketsAndColliders(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
try {
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
FastNMS.INSTANCE.toNMSEntityType(this.entityType), 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5() && this.scale != 1) {
|
||||
Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer<?>) (o) -> {});
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
||||
packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId[0], Collections.singletonList(attributeInstance)), false);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct custom hitbox spawn packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
||||
return new int[] {entityIdSupplier.get()};
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
Vector3f position = MiscUtils.getVector3f(arguments.getOrDefault("position", "0"));
|
||||
float scale = MiscUtils.getAsFloat(arguments.getOrDefault("scale", "1"));
|
||||
EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", (String) arguments.getOrDefault("entity-type", "slime")));
|
||||
if (entityType == null) {
|
||||
throw new IllegalArgumentException("EntityType not found: " + arguments.get("entity-type"));
|
||||
}
|
||||
return new CustomHitBox(HitBoxFactory.getSeats(arguments), position, entityType, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import org.joml.Vector3f;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class HappyGhastHitBox extends AbstractHitBox {
|
||||
@@ -29,7 +30,7 @@ public class HappyGhastHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpawnPackets(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets) {
|
||||
public void initPacketsAndColliders(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider) {
|
||||
// todo 乐魂
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -13,6 +14,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class InteractionHitBox extends AbstractHitBox {
|
||||
@@ -46,17 +48,13 @@ public class InteractionHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpawnPackets(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets) {
|
||||
public void initPacketsAndColliders(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
try {
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct hitbox spawn packet", e);
|
||||
}
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.ShulkerData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
@@ -22,13 +23,13 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
private final byte peek;
|
||||
private final boolean interactive;
|
||||
private final boolean interactionEntity;
|
||||
// todo或许还能做个方向,但是会麻烦点,和 yaw 有关
|
||||
private final Direction direction = Direction.UP;
|
||||
private final Direction direction;
|
||||
private final List<Object> cachedShulkerValues = new ArrayList<>();
|
||||
private final List<Object> cachedInteractionValues = new ArrayList<>();
|
||||
private final DirectionalShulkerSpawner spawner;
|
||||
|
||||
public ShulkerHitBox(Seat[] seats, Vector3f position, float scale, byte peek, boolean interactionEntity, boolean interactive) {
|
||||
public ShulkerHitBox(Seat[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive) {
|
||||
super(seats, position);
|
||||
this.direction = direction;
|
||||
this.scale = scale;
|
||||
this.peek = peek;
|
||||
this.interactive = interactive;
|
||||
@@ -36,22 +37,73 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
|
||||
ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues);
|
||||
ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues);
|
||||
// ShulkerData.AttachFace.addEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(direction), this.cachedShulkerValues);
|
||||
ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues);
|
||||
ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues);
|
||||
ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // 无ai
|
||||
ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // 不可见
|
||||
ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI
|
||||
ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible
|
||||
|
||||
if (this.interactionEntity) {
|
||||
// make it a litter bigger
|
||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue((getPhysicalPeek(peek * 0.01F) + 1) * scale + 0.01f, cachedInteractionValues);
|
||||
float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale;
|
||||
List<Object> cachedInteractionValues = new ArrayList<>();
|
||||
if (this.direction == Direction.UP) {
|
||||
Collider c = createCollider(Direction.UP);
|
||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
||||
this.spawner = (entityIds, x, y, z, yaw, offset, packets, collider) -> {
|
||||
collider.accept(c);
|
||||
if (interactionEntity) {
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
||||
}
|
||||
};
|
||||
} else if (this.direction == Direction.DOWN) {
|
||||
Collider c = createCollider(Direction.DOWN);
|
||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
||||
this.spawner = (entityIds, x, y, z, yaw, offset, packets, collider) -> {
|
||||
collider.accept(c);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(Reflections.instance$Direction$UP))), false);
|
||||
if (interactionEntity) {
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(scale + 0.01f, cachedInteractionValues);
|
||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
||||
this.spawner = (entityIds, x, y, z, yaw, offset, packets, collider) -> {
|
||||
Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw));
|
||||
Direction shulkerDirection = shulkerAnchor.opposite();
|
||||
collider.accept(this.createCollider(shulkerDirection));
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor)))), false);
|
||||
if (interactionEntity) {
|
||||
// first interaction
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
||||
// second interaction
|
||||
float distance = shulkerHeight - scale;
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Collider> optionalCollider() {
|
||||
public Collider createCollider(Direction d) {
|
||||
float peek = getPhysicalPeek(this.peek() * 0.01F);
|
||||
double x1 = -this.scale * 0.5;
|
||||
double y1 = 0.0;
|
||||
@@ -60,30 +112,30 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
double y2 = this.scale;
|
||||
double z2 = this.scale * 0.5;
|
||||
|
||||
double dx = (double) direction.stepX() * peek * (double) scale;
|
||||
double dx = (double) d.stepX() * peek * (double) this.scale;
|
||||
if (dx > 0) {
|
||||
x2 += dx;
|
||||
} else if (dx < 0) {
|
||||
x1 += dx;
|
||||
}
|
||||
double dy = (double) direction.stepY() * peek * (double) scale;
|
||||
double dy = (double) d.stepY() * peek * (double) this.scale;
|
||||
if (dy > 0) {
|
||||
y2 += dy;
|
||||
} else if (dy < 0) {
|
||||
y1 += dy;
|
||||
}
|
||||
double dz = (double) direction.stepZ() * peek * (double) scale;
|
||||
double dz = (double) d.stepZ() * peek * (double) this.scale;
|
||||
if (dz > 0) {
|
||||
z2 += dz;
|
||||
} else if (dz < 0) {
|
||||
z1 += dz;
|
||||
}
|
||||
return Optional.of(new Collider(
|
||||
return new Collider(
|
||||
true,
|
||||
position,
|
||||
this.position,
|
||||
new Vector3d(x1, y1, z1),
|
||||
new Vector3d(x2, y2, z2)
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
private static float getPhysicalPeek(float peek) {
|
||||
@@ -116,23 +168,25 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpawnPackets(int[] entityIds, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets) {
|
||||
public void initPacketsAndColliders(int[] entityIds, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
try {
|
||||
double originalY = y + offset.y;
|
||||
double integerPart = Math.floor(originalY);
|
||||
double fractionalPart = originalY - integerPart;
|
||||
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), false);
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), false);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
|
||||
// add passengers
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]), false);
|
||||
// fix some special occasions
|
||||
if (originalY != processedY) {
|
||||
double deltaY = originalY - processedY;
|
||||
short ya = (short) (deltaY * 8192);
|
||||
@@ -140,29 +194,34 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
entityIds[1], (short) 0, ya, (short) 0, true
|
||||
), false);
|
||||
}
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
// set shulker scale
|
||||
if (VersionHelper.isVersionNewerThan1_20_5() && this.scale != 1) {
|
||||
Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer<?>) (o) -> {});
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, scale);
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
||||
packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false);
|
||||
}
|
||||
if (this.interactionEntity) {
|
||||
// make it a litter lower
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
||||
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[2], List.copyOf(this.cachedInteractionValues)), true);
|
||||
}
|
||||
this.spawner.accept(entityIds, x, y, z, yaw, offset, packets, collider);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface DirectionalShulkerSpawner {
|
||||
|
||||
void accept(int[] entityIds, double x, double y, double z, float yaw, Vector3f offset, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
||||
if (this.interactionEntity) {
|
||||
// 展示实体 // 潜影贝 // 交互实体
|
||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
||||
if (this.direction.stepY() != 0) {
|
||||
// 展示实体 // 潜影贝 // 交互实体
|
||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
||||
} else {
|
||||
// 展示实体 // 潜影贝 // 交互实体1 // 交互实体2
|
||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
||||
}
|
||||
} else {
|
||||
// 展示实体 // 潜影贝
|
||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get()};
|
||||
@@ -181,9 +240,51 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
boolean interactionEntity = (boolean) arguments.getOrDefault("interaction-entity", true);
|
||||
return new ShulkerHitBox(
|
||||
HitBoxFactory.getSeats(arguments),
|
||||
position,
|
||||
position, directionEnum,
|
||||
scale, peek, interactionEntity, interactive
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) {
|
||||
switch (newDirection) {
|
||||
case NORTH -> {
|
||||
return switch (oldDirection) {
|
||||
case NORTH -> Direction.NORTH;
|
||||
case SOUTH -> Direction.SOUTH;
|
||||
case WEST -> Direction.EAST;
|
||||
case EAST -> Direction.WEST;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + oldDirection);
|
||||
};
|
||||
}
|
||||
case SOUTH -> {
|
||||
return switch (oldDirection) {
|
||||
case SOUTH -> Direction.NORTH;
|
||||
case WEST -> Direction.WEST;
|
||||
case EAST -> Direction.EAST;
|
||||
case NORTH -> Direction.SOUTH;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + oldDirection);
|
||||
};
|
||||
}
|
||||
case WEST -> {
|
||||
return switch (oldDirection) {
|
||||
case SOUTH -> Direction.EAST;
|
||||
case WEST -> Direction.NORTH;
|
||||
case EAST -> Direction.SOUTH;
|
||||
case NORTH -> Direction.WEST;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + oldDirection);
|
||||
};
|
||||
}
|
||||
case EAST -> {
|
||||
return switch (oldDirection) {
|
||||
case SOUTH -> Direction.WEST;
|
||||
case WEST -> Direction.SOUTH;
|
||||
case EAST -> Direction.NORTH;
|
||||
case NORTH -> Direction.EAST;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + oldDirection);
|
||||
};
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + newDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviors;
|
||||
import net.momirealms.craftengine.core.item.modifier.CustomModelDataModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModelModifier;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.pack.ResourceLocation;
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
@@ -51,7 +52,7 @@ public class BoneMealItemBehavior extends ItemBehavior {
|
||||
Object visualState = state.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||
if (!is) {
|
||||
sendSwing = true;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import net.momirealms.craftengine.bukkit.util.RecipeUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.*;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
@@ -12,8 +12,8 @@ import net.momirealms.craftengine.bukkit.util.ItemUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.bukkit.pack;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand;
|
||||
@@ -8,236 +7,162 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.pack.host.HostMode;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.impl.NoneHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
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.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private HostMode previousHostMode = HostMode.NONE;
|
||||
private UUID previousHostUUID;
|
||||
public static final String FAKE_URL = "https://127.0.0.1:65536";
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public BukkitPackManager(BukkitCraftEngine plugin) {
|
||||
public BukkitPackManager(BukkitCraftEngine plugin) {
|
||||
super(plugin, (rf, zp) -> {
|
||||
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
|
||||
EventUtils.fireAndForget(endEvent);
|
||||
});
|
||||
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
|
||||
EventUtils.fireAndForget(endEvent);
|
||||
});
|
||||
this.plugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
super.delayedInit();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
super.delayedInit();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.sendResourcePack(plugin.networkManager().getUser(event.getPlayer()), null);
|
||||
}
|
||||
}
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
if (Config.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
Player player = plugin.adapt(event.getPlayer());
|
||||
this.sendResourcePack(player);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onResourcePackStatus(PlayerResourcePackStatusEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) {
|
||||
event.getPlayer().kick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onResourcePackStatus(PlayerResourcePackStatusEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) {
|
||||
event.getPlayer().kick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
@Override
|
||||
public void load() {
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG || CraftEngine.instance().isInitializing()) {
|
||||
super.load();
|
||||
if (Config.sendPackOnJoin() && VersionHelper.isVersionNewerThan1_20_2() && !(resourcePackHost() instanceof NoneHost)) {
|
||||
this.modifyServerSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
if (Files.exists(resourcePackPath())) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
} else if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackSettings(Config.externalPackUUID(), Config.externalPackUrl(), Config.externalPackSha1(), Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.sendPackOnReload()) {
|
||||
if (this.previousHostMode == HostMode.SELF_HOST) {
|
||||
this.previousHostUUID = super.packUUID;
|
||||
}
|
||||
// unload packs if user changed to none host
|
||||
if (Config.hostMode() == HostMode.NONE && this.previousHostMode != HostMode.NONE) {
|
||||
unloadResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// load new external resource pack on reload
|
||||
if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
updateResourcePackForOnlinePlayers(null);
|
||||
} else {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// record previous host uuid here
|
||||
this.previousHostUUID = Config.externalPackUUID();
|
||||
}
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && this.previousHostMode != HostMode.SELF_HOST) {
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
ReloadCommand.RELOAD_PACK_FLAG = false;
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
updateResourcePackForOnlinePlayers(null);
|
||||
} else if (this.previousHostMode == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.previousHostMode = Config.hostMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
if (VersionHelper.isVersionNewerThan1_20_2() && this.previousHostMode != HostMode.NONE) {
|
||||
resetResourcePackSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateResourcePack() {
|
||||
// generate pack
|
||||
super.generateResourcePack();
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
// resend packs
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && Config.sendPackOnReload()) {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateResourcePackForOnlinePlayers(UUID previousUUID) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
BukkitServerPlayer serverPlayer = plugin.adapt(player);
|
||||
sendResourcePack(serverPlayer, previousUUID);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetResourcePackSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty());
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateResourcePackSettings(UUID uuid, String url, String sha1, boolean required, Component prompt) {
|
||||
if (!Config.sendPackOnJoin()) {
|
||||
resetResourcePackSettings();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
public void modifyServerSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Object info;
|
||||
if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(uuid, url, sha1, required, ComponentUtils.adventureToMinecraft(prompt));
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(new UUID(0, 0), FAKE_URL, "", Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
} else {
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(url + uuid, sha1, required, ComponentUtils.adventureToMinecraft(prompt));
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(FAKE_URL, "", Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
}
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.of(info));
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResourcePack(NetWorkUser user, @Nullable UUID previousPack) {
|
||||
if (Config.hostMode() == HostMode.NONE) return;
|
||||
String url;
|
||||
String sha1;
|
||||
UUID uuid;
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
url = ResourcePackHost.instance().url();
|
||||
sha1 = super.packHash;
|
||||
uuid = super.packUUID;
|
||||
if (!Files.exists(resourcePackPath())) return;
|
||||
} else {
|
||||
url = Config.externalPackUrl();
|
||||
sha1 = Config.externalPackSha1();
|
||||
uuid = Config.externalPackUUID();
|
||||
if (uuid.equals(previousPack)) return;
|
||||
}
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.resetServerSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object packPrompt = ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt());
|
||||
try {
|
||||
Object packPacket;
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, Config.kickOnDeclined(), Optional.of(packPrompt)
|
||||
);
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
} else {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
url + uuid, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
}
|
||||
if (user.decoderState() == ConnectionState.PLAY) {
|
||||
if (previousPack != null && VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
plugin.networkManager().sendPackets(user, List.of(Reflections.constructor$ClientboundResourcePackPopPacket.newInstance(Optional.of(previousPack)), packPacket));
|
||||
} else {
|
||||
user.sendPacket(packPacket, false);
|
||||
}
|
||||
} else {
|
||||
user.nettyChannel().writeAndFlush(packPacket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to send resource pack", e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
this.resetServerSettings();
|
||||
}
|
||||
|
||||
public void unloadResourcePackForOnlinePlayers(UUID uuid) {
|
||||
try {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
BukkitServerPlayer serverPlayer = plugin.adapt(player);
|
||||
if (serverPlayer.decoderState() == ConnectionState.PLAY) {
|
||||
serverPlayer.sendPacket(Reflections.constructor$ClientboundResourcePackPopPacket.newInstance(Optional.of(uuid)), true);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to unload online player resource pack", e);
|
||||
}
|
||||
}
|
||||
public void resetServerSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty());
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to reset resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAsyncResourcePackGenerate(AsyncResourcePackGenerateEvent event) {
|
||||
if (!Config.autoUpload()) return;
|
||||
uploadResourcePack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadResourcePack() {
|
||||
resourcePackHost().upload(Config.fileToUpload()).whenComplete((d, e) -> {
|
||||
if (e != null) {
|
||||
CraftEngine.instance().logger().warn("Failed to upload resource pack", e);
|
||||
return;
|
||||
}
|
||||
if (!Config.sendPackOnUpload()) return;
|
||||
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
|
||||
sendResourcePack(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResourcePack(Player player) {
|
||||
CompletableFuture<List<ResourcePackDownloadData>> future = resourcePackHost().requestResourcePackDownloadLink(player.uuid());
|
||||
future.thenAccept(dataList -> {
|
||||
if (player.isOnline()) {
|
||||
player.unloadCurrentResourcePack();
|
||||
if (dataList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (dataList.size() == 1) {
|
||||
ResourcePackDownloadData data = dataList.get(0);
|
||||
player.sendPacket(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()), true);
|
||||
player.addResourcePackUUID(data.uuid());
|
||||
} else {
|
||||
List<Object> packets = new ArrayList<>();
|
||||
for (ResourcePackDownloadData data : dataList) {
|
||||
packets.add(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()));
|
||||
player.addResourcePackUUID(data.uuid());
|
||||
}
|
||||
player.sendPackets(packets, true);
|
||||
}
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
CraftEngine.instance().logger().warn("Failed to send resource pack to player " + player.name(), throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new TotemAnimationCommand(this, plugin),
|
||||
new EnableResourceCommand(this, plugin),
|
||||
new DisableResourceCommand(this, plugin),
|
||||
new ListResourceCommand(this, plugin)
|
||||
new ListResourceCommand(this, plugin),
|
||||
new UploadPackCommand(this, plugin),
|
||||
new SendResourcePackCommand(this, plugin)
|
||||
));
|
||||
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
|
||||
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DebugSetBlockCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
@@ -33,7 +34,7 @@ public class DebugSetBlockCommand extends BukkitCommandFeature<CommandSender> {
|
||||
.required("id", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().blockManager().cachedSuggestions());
|
||||
return CompletableFuture.completedFuture(BlockStateParser.fillSuggestions(input.input(), input.cursor()).stream().map(Suggestion::suggestion).collect(Collectors.toList()));
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
|
||||
@@ -35,7 +35,6 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
}
|
||||
if (argument == ReloadArgument.CONFIG) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), false).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
@@ -49,13 +48,13 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
}
|
||||
} else if (argument == ReloadArgument.RECIPE) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime())
|
||||
);
|
||||
|
||||
});
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
|
||||
@@ -69,24 +68,30 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_SUCCESS, Component.text(packTime));
|
||||
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE);
|
||||
plugin().logger().warn("Failed to generate resource pack", e);
|
||||
}
|
||||
});
|
||||
} else if (argument == ReloadArgument.ALL) {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
try {
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAcceptAsync(reloadResult -> {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime() + packTime),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime()),
|
||||
Component.text(packTime)
|
||||
);
|
||||
try {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime() + packTime),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime()),
|
||||
Component.text(packTime)
|
||||
);
|
||||
} finally {
|
||||
RELOAD_PACK_FLAG = false;
|
||||
}
|
||||
}, plugin().scheduler().async());
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.bukkit.data.MultiplePlayerSelector;
|
||||
import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class SendResourcePackCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public SendResourcePackCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(FlagKeys.SILENT_FLAG)
|
||||
.required("player", MultiplePlayerSelectorParser.multiplePlayerSelectorParser(true))
|
||||
.handler(context -> {
|
||||
MultiplePlayerSelector selector = context.get("player");
|
||||
Collection<Player> players = selector.values();
|
||||
for (Player player : players) {
|
||||
BukkitServerPlayer bukkitServerPlayer = plugin().adapt(player);
|
||||
if (bukkitServerPlayer == null) continue;
|
||||
BukkitCraftEngine.instance().packManager().sendResourcePack(bukkitServerPlayer);
|
||||
}
|
||||
int size = players.size();
|
||||
if (size == 1) {
|
||||
String name = players.iterator().next().getName();
|
||||
handleFeedback(context, MessageConstants.COMMAND_SEND_RESOURCE_PACK_SUCCESS_SINGLE, Component.text(name));
|
||||
} else {
|
||||
handleFeedback(context, MessageConstants.COMMAND_SEND_RESOURCE_PACK_SUCCESS_MULTIPLE, Component.text(size));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "send_resource_pack";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
@@ -23,10 +19,6 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
.senderType(Player.class)
|
||||
.handler(context -> {
|
||||
Player player = context.sender();
|
||||
ItemStack itemStack = new ItemStack(Material.STONE);
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemStack);
|
||||
wrapped.lore(null);
|
||||
player.getInventory().addItem(wrapped.load());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
public class UploadPackCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public UploadPackCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.handler(context -> {
|
||||
ResourcePackHost host = plugin().packManager().resourcePackHost();
|
||||
if (host.canUpload()) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_ON_PROGRESS);
|
||||
plugin().packManager().uploadResourcePack();
|
||||
} else {
|
||||
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_FAILURE_NOT_SUPPORTED, Component.text(host.type().value()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "upload";
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import java.util.function.BiConsumer;
|
||||
public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener {
|
||||
private static BukkitNetworkManager instance;
|
||||
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> NMS_PACKET_HANDLERS = new HashMap<>();
|
||||
// only for game stage for the moment
|
||||
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> BYTE_BUFFER_PACKET_HANDLERS = new HashMap<>();
|
||||
|
||||
private static void registerNMSPacketConsumer(final TriConsumer<NetWorkUser, NMSPacketEvent, Object> function, @Nullable Class<?> packet) {
|
||||
@@ -130,6 +131,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerNMSPacketConsumer(PacketConsumers.PLAYER_INFO_UPDATE, Reflections.clazz$ClientboundPlayerInfoUpdatePacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.PLAYER_ACTION, Reflections.clazz$ServerboundPlayerActionPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SWING_HAND, Reflections.clazz$ServerboundSwingPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.HELLO_C2S, Reflections.clazz$ServerboundHelloPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.USE_ITEM_ON, Reflections.clazz$ServerboundUseItemOnPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_BLOCK, Reflections.clazz$ServerboundPickItemFromBlockPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, Reflections.clazz$ServerboundSetCreativeModeSlotPacket);
|
||||
@@ -144,6 +146,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_PUSH, Reflections.clazz$ClientboundResourcePackPushPacket);
|
||||
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
||||
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
|
||||
registerByteBufPacketConsumer(VersionHelper.isVersionNewerThan1_21_3() ? PacketConsumers.LEVEL_PARTICLE_1_21_3 : (VersionHelper.isVersionNewerThan1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket());
|
||||
@@ -286,7 +289,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
return hasModelEngine;
|
||||
}
|
||||
|
||||
public void receivePacket(@NotNull NetWorkUser player, Object packet) {
|
||||
public void simulatePacket(@NotNull NetWorkUser player, Object packet) {
|
||||
Channel channel = player.nettyChannel();
|
||||
if (channel.isOpen()) {
|
||||
List<String> handlerNames = channel.pipeline().names();
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUt
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.pack.BukkitPackManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
@@ -21,6 +22,8 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
@@ -51,6 +54,7 @@ public class PacketConsumers {
|
||||
private static int[] mappingsMOD;
|
||||
private static IntIdentityList BLOCK_LIST;
|
||||
private static IntIdentityList BIOME_LIST;
|
||||
private static final UUID EMPTY_UUID = new UUID(0, 0);
|
||||
|
||||
public static void init(Map<Integer, Integer> map, int registrySize) {
|
||||
mappings = new int[registrySize];
|
||||
@@ -1156,6 +1160,28 @@ public class PacketConsumers {
|
||||
}
|
||||
}
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HELLO_C2S = (user, event, packet) -> {
|
||||
try {
|
||||
BukkitServerPlayer player = (BukkitServerPlayer) user;
|
||||
String name = (String) Reflections.field$ServerboundHelloPacket$name.get(packet);
|
||||
player.setName(name);
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
UUID uuid = (UUID) Reflections.field$ServerboundHelloPacket$uuid.get(packet);
|
||||
player.setUUID(uuid);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<UUID> uuid = (Optional<UUID>) Reflections.field$ServerboundHelloPacket$uuid.get(packet);
|
||||
if (uuid.isPresent()) {
|
||||
player.setUUID(uuid.get());
|
||||
} else {
|
||||
player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ServerboundHelloPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SWING_HAND = (user, event, packet) -> {
|
||||
try {
|
||||
if (!user.isOnline()) return;
|
||||
@@ -2083,4 +2109,36 @@ public class PacketConsumers {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetScorePacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RESOURCE_PACK_PUSH = (user, event, packet) -> {
|
||||
try {
|
||||
if (!VersionHelper.isVersionNewerThan1_20_2()) return;
|
||||
// we should only handle fake urls
|
||||
String url = FastNMS.INSTANCE.field$ClientboundResourcePackPushPacket$url(packet);
|
||||
if (!url.equals(BukkitPackManager.FAKE_URL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
UUID packUUID = FastNMS.INSTANCE.field$ClientboundResourcePackPushPacket$uuid(packet);
|
||||
ResourcePackHost host = CraftEngine.instance().packManager().resourcePackHost();
|
||||
host.requestResourcePackDownloadLink(user.uuid()).thenAccept(dataList -> {
|
||||
if (dataList.isEmpty()) {
|
||||
user.simulatePacket(FastNMS.INSTANCE.constructor$ServerboundResourcePackPacket$SUCCESSFULLY_LOADED(packUUID));
|
||||
return;
|
||||
}
|
||||
for (ResourcePackDownloadData data : dataList) {
|
||||
Object newPacket = ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1());
|
||||
user.nettyChannel().writeAndFlush(newPacket);
|
||||
user.addResourcePackUUID(data.uuid());
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundResourcePackPushPacket", throwable);
|
||||
user.simulatePacket(FastNMS.INSTANCE.constructor$ServerboundResourcePackPacket$SUCCESSFULLY_LOADED(packUUID));
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundResourcePackPushPacket", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,17 +34,18 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BukkitServerPlayer extends Player {
|
||||
private final BukkitCraftEngine plugin;
|
||||
// connection state
|
||||
private final Channel channel;
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private ConnectionState decoderState;
|
||||
private ConnectionState encoderState;
|
||||
private final Set<UUID> resourcePackUUID = Collections.synchronizedSet(new HashSet<>());
|
||||
// some references
|
||||
private Reference<org.bukkit.entity.Player> playerRef;
|
||||
private Reference<Object> serverPlayerRef;
|
||||
@@ -94,6 +95,8 @@ public class BukkitServerPlayer extends Player {
|
||||
public void setPlayer(org.bukkit.entity.Player player) {
|
||||
this.playerRef = new WeakReference<>(player);
|
||||
this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
this.uuid = player.getUniqueId();
|
||||
this.name = player.getName();
|
||||
if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) {
|
||||
try {
|
||||
Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true);
|
||||
@@ -219,9 +222,24 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
if (player == null) return "Unknown";
|
||||
return player.getName();
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
if (this.name != null) return;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID uuid) {
|
||||
if (this.uuid != null) return;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,8 +268,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivePacket(Object packet) {
|
||||
this.plugin.networkManager().receivePacket(this, packet);
|
||||
public void simulatePacket(Object packet) {
|
||||
this.plugin.networkManager().simulatePacket(this, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -723,9 +741,29 @@ public class BukkitServerPlayer extends Player {
|
||||
this.hasClientMod = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcePackUUID(UUID uuid) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
this.resourcePackUUID.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView() {
|
||||
this.entityTypeView.clear();
|
||||
this.furnitureView.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadCurrentResourcePack() {
|
||||
if (!VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
return;
|
||||
}
|
||||
if (decoderState() == ConnectionState.PLAY && !this.resourcePackUUID.isEmpty()) {
|
||||
for (UUID u : this.resourcePackUUID) {
|
||||
sendPacket(FastNMS.INSTANCE.constructor$ClientboundResourcePackPopPacket(u), true);
|
||||
}
|
||||
this.resourcePackUUID.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,15 +625,15 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundAddEntityPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(clazz$ClientboundAddEntityPacket,
|
||||
int.class, UUID.class,
|
||||
double.class, double.class, double.class,
|
||||
float.class, float.class,
|
||||
clazz$EntityType,
|
||||
int.class, clazz$Vec3, double.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundAddEntityPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(clazz$ClientboundAddEntityPacket,
|
||||
// int.class, UUID.class,
|
||||
// double.class, double.class, double.class,
|
||||
// float.class, float.class,
|
||||
// clazz$EntityType,
|
||||
// int.class, clazz$Vec3, double.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundRemoveEntitiesPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(clazz$ClientboundRemoveEntitiesPacket, int[].class)
|
||||
@@ -685,10 +685,10 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundSetEntityDataPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(clazz$ClientboundSetEntityDataPacket,
|
||||
int.class, List.class)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundSetEntityDataPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(clazz$ClientboundSetEntityDataPacket,
|
||||
// int.class, List.class)
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$EntityDataSerializers = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -711,11 +711,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$EntityDataAccessor = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$EntityDataAccessor, int.class, clazz$EntityDataSerializer
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$EntityDataAccessor = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$EntityDataAccessor, int.class, clazz$EntityDataSerializer
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$SynchedEntityData = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -737,11 +737,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$SynchedEntityData$DataValue$create = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$SynchedEntityData$DataValue, clazz$SynchedEntityData$DataValue, clazz$EntityDataAccessor, Object.class
|
||||
)
|
||||
);
|
||||
// public static final Method method$SynchedEntityData$DataValue$create = requireNonNull(
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$SynchedEntityData$DataValue, clazz$SynchedEntityData$DataValue, clazz$EntityDataAccessor, Object.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Method method$Component$empty = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(
|
||||
@@ -6437,4 +6437,79 @@ public class Reflections {
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$CraftPlayer, new String[]{"setSimplifyContainerDesyncCheck"}, boolean.class
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundHelloPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.login.ServerboundHelloPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.login.PacketLoginInStart")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerboundHelloPacket$name = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, String.class, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerboundHelloPacket$uuid = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, UUID.class, 0
|
||||
) :
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, Optional.class, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundResourcePackPushPacket$id =
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ClientboundResourcePackPushPacket, UUID.class, 0
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundResourcePackPushPacket$prompt = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ClientboundResourcePackPushPacket,
|
||||
VersionHelper.isVersionNewerThan1_20_5() ? Optional.class : clazz$Component,
|
||||
0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundResourcePackPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInResourcePackStatus")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundResourcePackPacket$Action = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket$Action"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket$a"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundResourcePackPacket$Action"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInResourcePackStatus$EnumResourcePackStatus")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$ServerboundResourcePackPacket$Action$values = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(
|
||||
clazz$ServerboundResourcePackPacket$Action, clazz$ServerboundResourcePackPacket$Action.arrayType()
|
||||
)
|
||||
);
|
||||
|
||||
public static final Object instance$ServerboundResourcePackPacket$Action$ACCEPTED;
|
||||
|
||||
static {
|
||||
try {
|
||||
Object[] values = (Object[]) method$ServerboundResourcePackPacket$Action$values.invoke(null);
|
||||
instance$ServerboundResourcePackPacket$Action$ACCEPTED = values[3];
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Constructor<?> constructor$ServerboundResourcePackPacket = requireNonNull(
|
||||
field$ClientboundResourcePackPushPacket$id != null
|
||||
? ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, UUID.class, clazz$ServerboundResourcePackPacket$Action)
|
||||
: ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, clazz$ServerboundResourcePackPacket$Action)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ResourcePackUtils {
|
||||
|
||||
public static Object createPacket(UUID uuid, String url, String hash) {
|
||||
return FastNMS.INSTANCE.constructor$ClientboundResourcePackPushPacket(uuid, url, hash, Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
}
|
||||
}
|
||||
@@ -1198,133 +1198,133 @@ minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:
|
||||
# Suitable for making some surface decorations and crops.
|
||||
# Tripwire
|
||||
#minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]#
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
#minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]#
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true]
|
||||
########################################################################################################################################################################################################################
|
||||
# Can make transparent blocks, but the collision shape is relatively random. Not as useful as leaves.
|
||||
# Chorus Plant
|
||||
|
||||
@@ -51,6 +51,9 @@ dependencies {
|
||||
compileOnly("com.mojang:datafixerupper:${rootProject.properties["datafixerupper_version"]}")
|
||||
// Aho-Corasick java implementation
|
||||
compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}")
|
||||
// Amazon S3
|
||||
compileOnly("software.amazon.awssdk:s3:${rootProject.properties["amazon_awssdk_version"]}")
|
||||
compileOnly("software.amazon.awssdk:netty-nio-client:${rootProject.properties["amazon_awssdk_version"]}")
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -80,6 +83,8 @@ tasks {
|
||||
relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
|
||||
relocate("net.momirealms.sparrow.nbt", "net.momirealms.craftengine.libraries.nbt")
|
||||
relocate("net.jpountz", "net.momirealms.craftengine.libraries.jpountz") // lz4
|
||||
relocate("software.amazon.awssdk", "net.momirealms.craftengine.libraries.awssdk") // awssdk
|
||||
relocate("software.amazon.eventstream", "net.momirealms.craftengine.libraries.eventstream") // awssdk
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,183 @@ import net.momirealms.craftengine.core.util.StringReader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class BlockStateParser {
|
||||
private static final char START = '[';
|
||||
private static final char EQUAL = '=';
|
||||
private static final char SEPARATOR = ',';
|
||||
private static final char END = ']';
|
||||
|
||||
private final StringReader reader;
|
||||
private final int cursor;
|
||||
private final Set<String> suggestions = new HashSet<>();
|
||||
private final Set<String> used = new HashSet<>();
|
||||
|
||||
private String input;
|
||||
private int replaceCursor;
|
||||
private Holder<CustomBlock> block;
|
||||
private Collection<Property<?>> properties;
|
||||
private Property<?> property;
|
||||
|
||||
public BlockStateParser(String data, int cursor) {
|
||||
this.reader = new StringReader(data.toLowerCase());
|
||||
this.reader.setCursor(cursor);
|
||||
this.cursor = cursor;
|
||||
this.replaceCursor = cursor;
|
||||
}
|
||||
|
||||
public static Set<String> fillSuggestions(@NotNull String data) {
|
||||
return fillSuggestions(data, 0);
|
||||
}
|
||||
|
||||
public static Set<String> fillSuggestions(@NotNull String data, int cursor) {
|
||||
BlockStateParser parser = new BlockStateParser(data, cursor);
|
||||
parser.parse();
|
||||
return parser.suggestions;
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
readBlock();
|
||||
if (block == null) {
|
||||
suggestBlock();
|
||||
return;
|
||||
}
|
||||
|
||||
readProperties();
|
||||
if (properties.isEmpty()) return;
|
||||
|
||||
if (!reader.canRead())
|
||||
suggestStart();
|
||||
else if (reader.peek() == START) {
|
||||
reader.skip();
|
||||
suggestProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private void readBlock() {
|
||||
this.replaceCursor = reader.getCursor();
|
||||
this.input = reader.readUnquotedString();
|
||||
if (reader.canRead() && reader.peek() == ':') {
|
||||
reader.skip();
|
||||
input = input + ":" + reader.readUnquotedString();
|
||||
}
|
||||
BuiltInRegistries.BLOCK.get(Key.from(input)).ifPresent(block -> this.block = block);
|
||||
}
|
||||
|
||||
private void suggestBlock() {
|
||||
String front = readPrefix();
|
||||
for (Key key : BuiltInRegistries.BLOCK.keySet()) {
|
||||
String id = key.toString();
|
||||
if (id.contains(input)) {
|
||||
this.suggestions.add(front + id);
|
||||
}
|
||||
}
|
||||
this.suggestions.remove(front + "craftengine:empty");
|
||||
}
|
||||
|
||||
private void readProperties() {
|
||||
this.properties = this.block.value().properties();
|
||||
}
|
||||
|
||||
private void suggestStart() {
|
||||
this.replaceCursor = reader.getCursor();
|
||||
this.suggestions.add(readPrefix() + START);
|
||||
}
|
||||
|
||||
private void suggestProperties() {
|
||||
this.reader.skipWhitespace();
|
||||
this.replaceCursor = reader.getCursor();
|
||||
suggestPropertyNameAndEnd();
|
||||
|
||||
while (reader.canRead()) {
|
||||
if (used.isEmpty() && reader.peek() == SEPARATOR) return;
|
||||
if (reader.peek() == SEPARATOR) reader.skip();
|
||||
reader.skipWhitespace();
|
||||
if (reader.canRead() && reader.peek() == END) return;
|
||||
|
||||
replaceCursor = reader.getCursor();
|
||||
input = reader.readString();
|
||||
|
||||
property = block.value().getProperty(input);
|
||||
if (property == null) {
|
||||
suggestPropertyName();
|
||||
return;
|
||||
}
|
||||
if (used.contains(property.name().toLowerCase())) return;
|
||||
used.add(input);
|
||||
|
||||
reader.skipWhitespace();
|
||||
replaceCursor = reader.getCursor();
|
||||
suggestEqual();
|
||||
|
||||
if (!reader.canRead() || reader.peek() != EQUAL) return;
|
||||
|
||||
reader.skip();
|
||||
reader.skipWhitespace();
|
||||
replaceCursor = reader.getCursor();
|
||||
input = reader.readString();
|
||||
if (property.possibleValues().stream().noneMatch
|
||||
(value -> value.toString().equalsIgnoreCase(input))
|
||||
){
|
||||
suggestValue();
|
||||
return;
|
||||
}
|
||||
|
||||
reader.skipWhitespace();
|
||||
replaceCursor = reader.getCursor();
|
||||
if (reader.canRead()) {
|
||||
if (used.size() == properties.size()) return;
|
||||
if (reader.peek() != SEPARATOR) return;
|
||||
} else if (used.size() < properties.size()) {
|
||||
suggestSeparator();
|
||||
}
|
||||
}
|
||||
suggestEnd();
|
||||
}
|
||||
|
||||
private void suggestPropertyNameAndEnd() {
|
||||
if (!reader.getRemaining().isEmpty()) return;
|
||||
this.input = "";
|
||||
suggestEnd();
|
||||
suggestPropertyName();
|
||||
|
||||
}
|
||||
private void suggestPropertyName() {
|
||||
if (!reader.getRemaining().isEmpty()) return;
|
||||
String front = readPrefix();
|
||||
for (Property<?> p : properties) {
|
||||
if (!used.contains(p.name().toLowerCase()) && p.name().toLowerCase().startsWith(input)) {
|
||||
this.suggestions.add(front + p.name() + EQUAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void suggestEqual() {
|
||||
if (!reader.getRemaining().isEmpty()) return;
|
||||
this.suggestions.add(readPrefix() + EQUAL);
|
||||
}
|
||||
|
||||
private void suggestValue() {
|
||||
for (Object val : property.possibleValues()) {
|
||||
this.suggestions.add(readPrefix() + val.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
private void suggestSeparator() {
|
||||
this.suggestions.add(readPrefix() + SEPARATOR);
|
||||
}
|
||||
|
||||
private void suggestEnd() {
|
||||
this.suggestions.add(readPrefix() + END);
|
||||
}
|
||||
|
||||
private String readPrefix() {
|
||||
return reader.getString().substring(cursor, replaceCursor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ImmutableBlockState deserialize(@NotNull String data) {
|
||||
@@ -28,26 +202,35 @@ public class BlockStateParser {
|
||||
ImmutableBlockState defaultState = holder.value().defaultState();
|
||||
if (reader.canRead() && reader.peek() == '[') {
|
||||
reader.skip();
|
||||
while (reader.canRead() && reader.peek() != ']') {
|
||||
while (reader.canRead()) {
|
||||
reader.skipWhitespace();
|
||||
if (reader.peek() == ']') break;
|
||||
String propertyName = reader.readUnquotedString();
|
||||
reader.skipWhitespace();
|
||||
if (!reader.canRead() || reader.peek() != '=') {
|
||||
return null;
|
||||
}
|
||||
reader.skip();
|
||||
reader.skipWhitespace();
|
||||
String propertyValue = reader.readUnquotedString();
|
||||
Property<?> property = holder.value().getProperty(propertyName);
|
||||
if (property != null) {
|
||||
Optional<?> optionalValue = property.optional(propertyValue);
|
||||
if (optionalValue.isEmpty()) {
|
||||
defaultState = ImmutableBlockState.with(defaultState, property, property.defaultValue());
|
||||
//defaultState = ImmutableBlockState.with(defaultState, property, property.defaultValue());
|
||||
return null;
|
||||
} else {
|
||||
defaultState = ImmutableBlockState.with(defaultState, property, optionalValue.get());
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
reader.skipWhitespace();
|
||||
if (reader.canRead() && reader.peek() == ',') {
|
||||
reader.skip();
|
||||
}
|
||||
}
|
||||
reader.skipWhitespace();
|
||||
if (reader.canRead() && reader.peek() == ']') {
|
||||
reader.skip();
|
||||
} else {
|
||||
|
||||
@@ -58,7 +58,6 @@ public class CustomFurniture {
|
||||
|
||||
public record Placement(FurnitureElement[] elements,
|
||||
HitBox[] hitBoxes,
|
||||
Collider[] colliders,
|
||||
RotationRule rotationRule,
|
||||
AlignmentRule alignmentRule,
|
||||
Optional<ExternalModel> externalModel) {
|
||||
|
||||
@@ -21,5 +21,5 @@ public interface FurnitureElement {
|
||||
|
||||
Vector3f position();
|
||||
|
||||
void addSpawnPackets(int entityId, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<Object> packets);
|
||||
void initPackets(int entityId, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<Object> packets);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public class FurnitureSettings {
|
||||
}
|
||||
|
||||
public static FurnitureSettings applyModifiers(FurnitureSettings settings, Map<String, Object> map) {
|
||||
if (map == null) return settings;
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
FurnitureSettings.Modifier.Factory factory = FurnitureSettings.Modifiers.FACTORIES.get(entry.getKey());
|
||||
if (factory != null) {
|
||||
|
||||
@@ -4,23 +4,19 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface HitBox {
|
||||
|
||||
Key type();
|
||||
|
||||
void addSpawnPackets(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets);
|
||||
void initPacketsAndColliders(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider);
|
||||
|
||||
int[] acquireEntityIds(Supplier<Integer> entityIdSupplier);
|
||||
|
||||
Seat[] seats();
|
||||
|
||||
Vector3f position();
|
||||
|
||||
default Optional<Collider> optionalCollider() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public class HitBoxTypes {
|
||||
public static final Key INTERACTION = Key.of("minecraft:interaction");
|
||||
public static final Key SHULKER = Key.of("minecraft:shulker");
|
||||
public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast");
|
||||
public static final Key CUSTOM = Key.of("minecraft:custom");
|
||||
|
||||
public static void register(Key key, HitBoxFactory factory) {
|
||||
Holder.Reference<HitBoxFactory> holder = ((WritableRegistry<HitBoxFactory>) BuiltInRegistries.HITBOX_FACTORY)
|
||||
|
||||
@@ -82,4 +82,6 @@ public abstract class Player extends Entity implements NetWorkUser {
|
||||
public abstract void closeInventory();
|
||||
|
||||
public abstract void clearView();
|
||||
|
||||
public abstract void unloadCurrentResourcePack();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class BitmapImage implements FontProvider {
|
||||
|
||||
public int codepointAt(int row, int column) {
|
||||
if (!isValidCoordinate(row, column)) {
|
||||
throw new IndexOutOfBoundsException("Invalid index: (" + row + ", " + column + ")");
|
||||
throw new IndexOutOfBoundsException("Invalid index: (" + row + ", " + column + ") for image " + id());
|
||||
}
|
||||
return codepointGrid[row][column];
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.modifier.*;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModel;
|
||||
import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
|
||||
@@ -2,9 +2,9 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModel;
|
||||
import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
|
||||
@@ -7,10 +7,12 @@ import net.momirealms.craftengine.core.font.BitmapImage;
|
||||
import net.momirealms.craftengine.core.font.Font;
|
||||
import net.momirealms.craftengine.core.item.EquipmentData;
|
||||
import net.momirealms.craftengine.core.pack.conflict.resolution.ConditionalResolution;
|
||||
import net.momirealms.craftengine.core.pack.host.HostMode;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.pack.host.impl.NoneHost;
|
||||
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModel;
|
||||
import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
|
||||
import net.momirealms.craftengine.core.pack.obfuscation.ObfA;
|
||||
@@ -34,10 +36,7 @@ import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
@@ -68,10 +67,8 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
private final Map<String, Pack> loadedPacks = new HashMap<>();
|
||||
private final Map<String, ConfigSectionParser> sectionParsers = new HashMap<>();
|
||||
private final TreeMap<ConfigSectionParser, List<CachedConfig>> cachedConfigs = new TreeMap<>();
|
||||
|
||||
protected BiConsumer<Path, Path> zipGenerator;
|
||||
protected String packHash;
|
||||
protected UUID packUUID;
|
||||
protected ResourcePackHost resourcePackHost;
|
||||
|
||||
public AbstractPackManager(CraftEngine plugin, BiConsumer<Path, Path> eventDispatcher) {
|
||||
this.plugin = plugin;
|
||||
@@ -150,16 +147,19 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.calculateHash();
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
Path path = Config.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(Config.hostResourcePackPath()) : Path.of(Config.hostResourcePackPath());
|
||||
ResourcePackHost.instance().enable(Config.hostIP(), Config.hostPort(), path);
|
||||
ResourcePackHost.instance().setRateLimit(Config.requestRate(), Config.requestInterval(), TimeUnit.SECONDS);
|
||||
List<Map<?, ?>> list = Config.instance().settings().getMapList("resource-pack.delivery.hosting");
|
||||
if (list == null || list.isEmpty()) {
|
||||
this.resourcePackHost = NoneHost.INSTANCE;
|
||||
} else {
|
||||
ResourcePackHost.instance().disable();
|
||||
this.resourcePackHost = ResourcePackHosts.fromMap(MiscUtils.castToMap(list.get(0), false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePackHost resourcePackHost() {
|
||||
return this.resourcePackHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadResources(boolean recipe) {
|
||||
this.loadPacks();
|
||||
@@ -222,10 +222,6 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Path selfHostPackPath() {
|
||||
return Config.hostResourcePackPath().startsWith(".") ? plugin.dataFolderPath().resolve(Config.hostResourcePackPath()) : Path.of(Config.hostResourcePackPath());
|
||||
}
|
||||
|
||||
private void loadPacks() {
|
||||
Path resourcesFolder = this.plugin.dataFolderPath().resolve("resources");
|
||||
try {
|
||||
@@ -264,6 +260,9 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
|
||||
private void saveDefaultConfigs() {
|
||||
// internal
|
||||
plugin.saveResource("resources/remove_shulker_head/resourcepack/assets/minecraft/textures/entity/shulker/shulker_white.png");
|
||||
plugin.saveResource("resources/remove_shulker_head/pack.yml");
|
||||
// internal
|
||||
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/models/block/default_chorus_plant.json");
|
||||
plugin.saveResource("resources/internal/pack.yml");
|
||||
@@ -310,6 +309,8 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
plugin.saveResource("resources/default/configuration/block_name.yml");
|
||||
// categories
|
||||
plugin.saveResource("resources/default/configuration/categories.yml");
|
||||
// for mods
|
||||
plugin.saveResource("resources/default/configuration/fix_client_visual.yml");
|
||||
// icons
|
||||
plugin.saveResource("resources/default/configuration/icons.yml");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/font/image/icons.png");
|
||||
@@ -526,24 +527,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
this.plugin.logger().info("Finished generating resource pack in " + (end - start) + "ms");
|
||||
|
||||
this.eventDispatcher.accept(generatedPackPath, zipFile);
|
||||
this.calculateHash();
|
||||
}
|
||||
|
||||
private void calculateHash() {
|
||||
Path zipFile = selfHostPackPath();
|
||||
if (Files.exists(zipFile)) {
|
||||
try {
|
||||
this.packHash = computeSHA1(zipFile);
|
||||
this.packUUID = UUID.nameUUIDFromBytes(this.packHash.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
this.plugin.logger().severe("Error calculating resource pack hash", e);
|
||||
}
|
||||
} else {
|
||||
this.packHash = "";
|
||||
this.packUUID = UUID.nameUUIDFromBytes("EMPTY".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private void generateParticle(Path generatedPackPath) {
|
||||
@@ -1137,23 +1121,6 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
}
|
||||
|
||||
protected String computeSHA1(Path path) throws IOException, NoSuchAlgorithmException {
|
||||
InputStream file = Files.newInputStream(path);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = file.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
file.close();
|
||||
|
||||
StringBuilder hexString = new StringBuilder(40);
|
||||
for (byte b : digest.digest()) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
private List<Pair<Path, List<Path>>> mergeFolder(Collection<Path> sourceFolders, Path targetFolder) throws IOException {
|
||||
Map<Path, List<Path>> conflictChecker = new HashMap<>();
|
||||
for (Path sourceFolder : sourceFolders) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.momirealms.craftengine.core.pack;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,4 +35,10 @@ public interface PackManager extends Manageable {
|
||||
void generateResourcePack();
|
||||
|
||||
Path resourcePackPath();
|
||||
|
||||
ResourcePackHost resourcePackHost();
|
||||
|
||||
void uploadResourcePack();
|
||||
|
||||
void sendResourcePack(Player player);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package net.momirealms.craftengine.core.pack.host;
|
||||
|
||||
public enum HostMode {
|
||||
SELF_HOST,
|
||||
EXTERNAL_HOST,
|
||||
NONE
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.momirealms.craftengine.core.pack.host;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record ResourcePackDownloadData(String url, UUID uuid, String sha1) {
|
||||
|
||||
public static ResourcePackDownloadData of(String url, UUID uuid, String sha1) {
|
||||
return new ResourcePackDownloadData(url, uuid, sha1);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +1,19 @@
|
||||
package net.momirealms.craftengine.core.pack.host;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ResourcePackHost {
|
||||
private static ResourcePackHost instance;
|
||||
private HttpServer server;
|
||||
private String ip;
|
||||
private int port;
|
||||
private Path resourcePackPath;
|
||||
private final ConcurrentHashMap<String, IpAccessRecord> ipAccessMap = new ConcurrentHashMap<>();
|
||||
private int rateLimit = 1;
|
||||
private long rateLimitInterval = 1000;
|
||||
public interface ResourcePackHost {
|
||||
|
||||
public String url() {
|
||||
return Config.hostProtocol() + "://" + ip + ":" + port + "/";
|
||||
}
|
||||
CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player);
|
||||
|
||||
public void enable(String ip, int port, Path resourcePackPath) {
|
||||
if (isAlive() && ip.equals(this.ip) && port == this.port && resourcePackPath.equals(this.resourcePackPath)) {
|
||||
return;
|
||||
}
|
||||
if (server != null) {
|
||||
disable();
|
||||
}
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
this.resourcePackPath = resourcePackPath;
|
||||
CompletableFuture<Void> upload(Path resourcePackPath);
|
||||
|
||||
try {
|
||||
server = HttpServer.create(new InetSocketAddress("::", port), 0);
|
||||
server.createContext("/", new ResourcePackHandler());
|
||||
server.setExecutor(Executors.newCachedThreadPool());
|
||||
server.start();
|
||||
CraftEngine.instance().logger().info("HTTP resource pack server running on " + ip + ":" + port);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to start HTTP server", e);
|
||||
}
|
||||
}
|
||||
boolean canUpload();
|
||||
|
||||
public void disable() {
|
||||
if (server != null) {
|
||||
server.stop(0);
|
||||
server = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return server != null;
|
||||
}
|
||||
|
||||
public static ResourcePackHost instance() {
|
||||
if (instance == null) {
|
||||
instance = new ResourcePackHost();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setRateLimit(int rateLimit, long rateLimitInterval, TimeUnit timeUnit) {
|
||||
this.rateLimit = rateLimit;
|
||||
this.rateLimitInterval = timeUnit.toMillis(rateLimitInterval);
|
||||
}
|
||||
|
||||
private class ResourcePackHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
if (Config.denyNonMinecraftRequest()) {
|
||||
String userAgent = exchange.getRequestHeaders().getFirst("User-Agent");
|
||||
if (userAgent == null || !userAgent.startsWith("Minecraft Java/")) {
|
||||
CraftEngine.instance().debug(() -> "Blocked non-Minecraft Java client. User-Agent: " + userAgent);
|
||||
sendError(exchange, 403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String clientIp = exchange.getRemoteAddress().getAddress().getHostAddress();
|
||||
|
||||
IpAccessRecord record = ipAccessMap.compute(clientIp, (k, v) -> {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (v == null || currentTime - v.lastAccessTime > rateLimitInterval) {
|
||||
return new IpAccessRecord(currentTime, 1);
|
||||
} else {
|
||||
v.accessCount++;
|
||||
return v;
|
||||
}
|
||||
});
|
||||
|
||||
if (record.accessCount > rateLimit) {
|
||||
CraftEngine.instance().debug(() -> "Rate limit exceeded for IP: " + clientIp);
|
||||
sendError(exchange, 429);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Files.exists(resourcePackPath)) {
|
||||
CraftEngine.instance().logger().warn("ResourcePack not found: " + resourcePackPath);
|
||||
sendError(exchange, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
exchange.getResponseHeaders().set("Content-Type", "application/zip");
|
||||
exchange.getResponseHeaders().set("Content-Length", String.valueOf(Files.size(resourcePackPath)));
|
||||
exchange.sendResponseHeaders(200, Files.size(resourcePackPath));
|
||||
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
Files.copy(resourcePackPath, os);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to send pack", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendError(HttpExchange exchange, int code) throws IOException {
|
||||
exchange.sendResponseHeaders(code, 0);
|
||||
exchange.getResponseBody().close();
|
||||
}
|
||||
}
|
||||
|
||||
private static class IpAccessRecord {
|
||||
long lastAccessTime;
|
||||
int accessCount;
|
||||
|
||||
IpAccessRecord(long lastAccessTime, int accessCount) {
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
this.accessCount = accessCount;
|
||||
}
|
||||
}
|
||||
Key type();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.momirealms.craftengine.core.pack.host;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ResourcePackHostFactory {
|
||||
|
||||
ResourcePackHost create(Map<String, Object> arguments);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.momirealms.craftengine.core.pack.host;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.host.impl.*;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.registry.Registries;
|
||||
import net.momirealms.craftengine.core.registry.WritableRegistry;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceKey;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ResourcePackHosts {
|
||||
public static final Key NONE = Key.of("craftengine:none");
|
||||
public static final Key SELF = Key.of("craftengine:self");
|
||||
public static final Key EXTERNAL = Key.of("craftengine:external");
|
||||
public static final Key LOBFILE = Key.of("craftengine:lobfile");
|
||||
public static final Key S3 = Key.of("craftengine:s3");
|
||||
public static final Key ALIST = Key.of("craftengine:alist");
|
||||
public static final Key DROPBOX = Key.of("craftengine:dropbox");
|
||||
public static final Key ONEDRIVE = Key.of("craftengine:onedrive");
|
||||
|
||||
static {
|
||||
register(NONE, NoneHost.FACTORY);
|
||||
register(SELF, SelfHost.FACTORY);
|
||||
register(EXTERNAL, ExternalHost.FACTORY);
|
||||
register(LOBFILE, LobFileHost.FACTORY);
|
||||
register(S3, S3Host.FACTORY);
|
||||
register(ALIST, AlistHost.FACTORY);
|
||||
register(DROPBOX, DropboxHost.FACTORY);
|
||||
register(ONEDRIVE, OneDriveHost.FACTORY);
|
||||
}
|
||||
|
||||
public static void register(Key key, ResourcePackHostFactory factory) {
|
||||
Holder.Reference<ResourcePackHostFactory> holder = ((WritableRegistry<ResourcePackHostFactory>) BuiltInRegistries.RESOURCE_PACK_HOST_FACTORY)
|
||||
.registerForHolder(new ResourceKey<>(Registries.RESOURCE_PACK_HOST_FACTORY.location(), key));
|
||||
holder.bindValue(factory);
|
||||
}
|
||||
|
||||
public static ResourcePackHost fromMap(Map<String, Object> map) {
|
||||
String type = (String) map.get("type");
|
||||
if (type == null) {
|
||||
throw new NullPointerException("host type cannot be null");
|
||||
}
|
||||
Key key = Key.withDefaultNamespace(type, "craftengine");
|
||||
ResourcePackHostFactory factory = BuiltInRegistries.RESOURCE_PACK_HOST_FACTORY.getValue(key);
|
||||
if (factory == null) {
|
||||
throw new IllegalArgumentException("Unknown resource pack host type: " + type);
|
||||
}
|
||||
return factory.create(map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class AlistHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final String apiUrl;
|
||||
private final String userName;
|
||||
private final String password;
|
||||
private final String filePassword;
|
||||
private final String otpCode;
|
||||
private final Duration jwtTokenExpiration;
|
||||
private final String uploadPath;
|
||||
private final boolean disableUpload;
|
||||
private final ProxySelector proxy;
|
||||
private Pair<String, Date> jwtToken;
|
||||
private String cachedSha1;
|
||||
|
||||
public AlistHost(String apiUrl,
|
||||
String userName,
|
||||
String password,
|
||||
String filePassword,
|
||||
String otpCode,
|
||||
Duration jwtTokenExpiration,
|
||||
String uploadPath,
|
||||
boolean disableUpload,
|
||||
ProxySelector proxy) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
this.filePassword = filePassword;
|
||||
this.otpCode = otpCode;
|
||||
this.jwtTokenExpiration = jwtTokenExpiration;
|
||||
this.uploadPath = uploadPath;
|
||||
this.disableUpload = disableUpload;
|
||||
this.proxy = proxy;
|
||||
this.readCacheFromDisk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.ALIST;
|
||||
}
|
||||
|
||||
private void readCacheFromDisk() {
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("alist.cache");
|
||||
if (!Files.exists(cachePath)) return;
|
||||
|
||||
try (InputStream is = Files.newInputStream(cachePath)) {
|
||||
Map<String, String> cache = GsonHelper.get().fromJson(
|
||||
new InputStreamReader(is),
|
||||
new TypeToken<Map<String, String>>(){}.getType()
|
||||
);
|
||||
|
||||
this.cachedSha1 = cache.get("sha1");
|
||||
|
||||
CraftEngine.instance().logger().info("[Alist] Loaded cached resource pack metadata");
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[Alist] Failed to load cache from disk: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCacheToDisk() {
|
||||
Map<String, String> cache = new HashMap<>();
|
||||
cache.put("sha1", this.cachedSha1 != null ? this.cachedSha1 : "");
|
||||
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("alist.cache");
|
||||
try {
|
||||
Files.writeString(
|
||||
cachePath,
|
||||
GsonHelper.get().toJson(cache),
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[Alist] Failed to persist cache to disk: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
CompletableFuture<List<ResourcePackDownloadData>> future = new CompletableFuture<>();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(this.apiUrl + "/api/fs/get"))
|
||||
.header("Authorization", getOrRefreshJwtToken())
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(getRequestResourcePackDownloadLinkPost())
|
||||
.build();
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> handleResourcePackDownloadLinkResponse(response, future))
|
||||
.exceptionally(ex -> {
|
||||
CraftEngine.instance().logger().severe("[Alist] Failed to retrieve resource pack download URL", ex);
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
if (this.disableUpload) {
|
||||
this.cachedSha1 = "";
|
||||
saveCacheToDisk();
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(this.apiUrl + "/api/fs/put"))
|
||||
.header("Authorization", getOrRefreshJwtToken())
|
||||
.header("File-Path", URLEncoder.encode(this.uploadPath, StandardCharsets.UTF_8)
|
||||
.replace("/", "%2F"))
|
||||
.header("overwrite", "true")
|
||||
.header("password", this.filePassword)
|
||||
.header("Content-Type", "application/x-zip-compressed")
|
||||
.PUT(HttpRequest.BodyPublishers.ofFile(resourcePackPath))
|
||||
.build();
|
||||
long requestStart = System.currentTimeMillis();
|
||||
CraftEngine.instance().logger().info("[Alist] Initiating resource pack upload...");
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> {
|
||||
long uploadTime = System.currentTimeMillis() - requestStart;
|
||||
if (response.statusCode() == 200) {
|
||||
this.cachedSha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
|
||||
saveCacheToDisk();
|
||||
CraftEngine.instance().logger().info("[Alist] Successfully uploaded resource pack in " + uploadTime + " ms");
|
||||
future.complete(null);
|
||||
} else {
|
||||
future.completeExceptionally(new RuntimeException("Upload failed with status code: " + response.statusCode()));
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
long uploadTime = System.currentTimeMillis() - requestStart;
|
||||
CraftEngine.instance().logger().severe(
|
||||
"[Alist] Resource pack upload failed after " + uploadTime + " ms", ex);
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("[Alist] Failed to upload resource pack: " + e.getMessage());
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getOrRefreshJwtToken() {
|
||||
if (this.jwtToken == null || this.jwtToken.right().before(new Date())) {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(this.apiUrl + "/api/auth/login"))
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(getLoginPost())
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() != 200) {
|
||||
CraftEngine.instance().logger().warn("[Alist] Authentication failed (HTTP " + response.statusCode() + "): " + response.body());
|
||||
return null;
|
||||
}
|
||||
JsonObject jsonData = GsonHelper.parseJsonToJsonObject(response.body());
|
||||
JsonElement code = jsonData.get("code");
|
||||
if (code.isJsonPrimitive() && code.getAsJsonPrimitive().isNumber() && code.getAsJsonPrimitive().getAsInt() == 200) {
|
||||
JsonElement data = jsonData.get("data");
|
||||
if (data.isJsonObject()) {
|
||||
JsonObject jsonObj = data.getAsJsonObject();
|
||||
this.jwtToken = Pair.of(
|
||||
jsonObj.getAsJsonPrimitive("token").getAsString(),
|
||||
new Date(System.currentTimeMillis() + this.jwtTokenExpiration.toMillis())
|
||||
);
|
||||
return this.jwtToken.left();
|
||||
}
|
||||
CraftEngine.instance().logger().warn("[Alist] Invalid JWT response format: " + response.body());
|
||||
return null;
|
||||
}
|
||||
CraftEngine.instance().logger().warn("[Alist] Authentication rejected: " + response.body());
|
||||
return null;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
CraftEngine.instance().logger().warn("[Alist] JWT token acquisition failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this.jwtToken.left();
|
||||
}
|
||||
|
||||
private HttpRequest.BodyPublisher getLoginPost() {
|
||||
String body = "{\"username\":\"" + this.userName + "\",\"password\":\"" + this.password + "\"";
|
||||
if (this.otpCode != null && !this.otpCode.isEmpty()) {
|
||||
body += ",\"otp_code\":\"" + this.otpCode + "\"";
|
||||
}
|
||||
body += "}";
|
||||
return HttpRequest.BodyPublishers.ofString(body);
|
||||
}
|
||||
|
||||
private HttpRequest.BodyPublisher getRequestResourcePackDownloadLinkPost() {
|
||||
String body = "{\"path\":\"" + this.uploadPath + "\",\"password\":\"" + this.filePassword + "\"}";
|
||||
return HttpRequest.BodyPublishers.ofString(body);
|
||||
}
|
||||
|
||||
private void handleResourcePackDownloadLinkResponse(
|
||||
HttpResponse<String> response, CompletableFuture<List<ResourcePackDownloadData>> future) {
|
||||
if (response.statusCode() == 200) {
|
||||
JsonObject json = GsonHelper.parseJsonToJsonObject(response.body());
|
||||
JsonElement code = json.get("code");
|
||||
if (code.isJsonPrimitive() && code.getAsJsonPrimitive().isNumber() && code.getAsJsonPrimitive().getAsInt() == 200) {
|
||||
JsonElement data = json.get("data");
|
||||
if (data.isJsonObject()) {
|
||||
JsonObject dataObj = data.getAsJsonObject();
|
||||
boolean isDir = dataObj.getAsJsonPrimitive("is_dir").getAsBoolean();
|
||||
if (!isDir) {
|
||||
String url = dataObj.getAsJsonPrimitive("raw_url").getAsString();
|
||||
if ((this.cachedSha1 == null || this.cachedSha1.isEmpty()) && this.disableUpload) {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<InputStream> responseHash = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
try (InputStream inputStream = responseHash.body()) {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, len);
|
||||
}
|
||||
byte[] digest = md.digest();
|
||||
this.cachedSha1 = HexFormat.of().formatHex(digest);
|
||||
saveCacheToDisk();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
future.completeExceptionally(new RuntimeException("Failed to calculate SHA-1 hash algorithm", e));
|
||||
return;
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
future.completeExceptionally(new RuntimeException("Failed to retrieve remote resource pack for hashing", e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
UUID uuid = UUID.nameUUIDFromBytes(Objects.requireNonNull(this.cachedSha1).getBytes(StandardCharsets.UTF_8));
|
||||
future.complete(List.of(new ResourcePackDownloadData(url, uuid, this.cachedSha1)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
future.completeExceptionally(
|
||||
new RuntimeException("Failed to obtain resource pack download URL (HTTP " + response.statusCode() + "): " + response.body()));
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
|
||||
String apiUrl = (String) arguments.get("api-url");
|
||||
if (apiUrl == null || apiUrl.isEmpty()) {
|
||||
throw new IllegalArgumentException("'api-url' cannot be empty for Alist host");
|
||||
}
|
||||
String userName = useEnv ? System.getenv("CE_ALIST_USERNAME") : (String) arguments.get("username");
|
||||
if (userName == null || userName.isEmpty()) {
|
||||
throw new IllegalArgumentException("'username' cannot be empty for Alist host");
|
||||
}
|
||||
String password = useEnv ? System.getenv("CE_ALIST_PASSWORD") : (String) arguments.get("password");
|
||||
if (password == null || password.isEmpty()) {
|
||||
throw new IllegalArgumentException("'password' cannot be empty for Alist host");
|
||||
}
|
||||
String filePassword = useEnv ? System.getenv("CE_ALIST_FILE_PASSWORD") : (String) arguments.getOrDefault("file-password", "");
|
||||
String otpCode = (String) arguments.get("otp-code");
|
||||
Duration jwtTokenExpiration = Duration.ofHours((int) arguments.getOrDefault("jwt-token-expiration", 48));
|
||||
String uploadPath = (String) arguments.get("upload-path");
|
||||
if (uploadPath == null || uploadPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("'upload-path' cannot be empty for Alist host");
|
||||
}
|
||||
boolean disableUpload = (boolean) arguments.getOrDefault("disable-upload", false);
|
||||
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
|
||||
return new AlistHost(apiUrl, userName, password, filePassword, otpCode, jwtTokenExpiration, uploadPath, disableUpload, proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.HashUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class DropboxHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final String appKey;
|
||||
private final String appSecret;
|
||||
private final String uploadPath;
|
||||
private final ProxySelector proxy;
|
||||
private final ReentrantLock tokenLock = new ReentrantLock();
|
||||
|
||||
private volatile String accessToken;
|
||||
private volatile String refreshToken;
|
||||
private volatile long expiresAt;
|
||||
private String url;
|
||||
private String sha1;
|
||||
|
||||
public DropboxHost(String appKey, String appSecret, String refreshToken, String uploadPath, ProxySelector proxy) {
|
||||
this.appKey = appKey;
|
||||
this.appSecret = appSecret;
|
||||
this.refreshToken = refreshToken;
|
||||
this.uploadPath = uploadPath;
|
||||
this.proxy = proxy;
|
||||
readCacheFromDisk();
|
||||
}
|
||||
|
||||
public void readCacheFromDisk() {
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("dropbox.cache");
|
||||
if (!Files.exists(cachePath)) return;
|
||||
|
||||
try (InputStream is = Files.newInputStream(cachePath)) {
|
||||
JsonObject cache = GsonHelper.parseJsonToJsonObject(new String(is.readAllBytes(), StandardCharsets.UTF_8));
|
||||
|
||||
this.url = getString(cache, "url");
|
||||
this.sha1 = getString(cache, "sha1");
|
||||
this.refreshToken = getString(cache, "refresh_token");
|
||||
this.accessToken = getString(cache, "access_token");
|
||||
this.expiresAt = getLong(cache, "expires_at");
|
||||
|
||||
CraftEngine.instance().logger().info("[Dropbox] Loaded cached resource pack info");
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("[Dropbox] Failed to load cache", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCacheToDisk() {
|
||||
JsonObject cache = new JsonObject();
|
||||
cache.addProperty("url", this.url);
|
||||
cache.addProperty("sha1", this.sha1);
|
||||
cache.addProperty("refresh_token", this.refreshToken);
|
||||
cache.addProperty("access_token", this.accessToken);
|
||||
cache.addProperty("expires_at", this.expiresAt);
|
||||
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("dropbox.cache");
|
||||
try {
|
||||
Files.writeString(
|
||||
cachePath,
|
||||
GsonHelper.get().toJson(cache),
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("[Dropbox] Failed to save cache", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.DROPBOX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
return CompletableFuture.completedFuture(Collections.singletonList(ResourcePackDownloadData.of(
|
||||
this.url, UUID.nameUUIDFromBytes(this.sha1.getBytes(StandardCharsets.UTF_8)), this.sha1
|
||||
)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try {
|
||||
String validToken = getOrRefreshToken();
|
||||
this.sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
|
||||
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
JsonObject apiArg = new JsonObject();
|
||||
apiArg.addProperty("path", this.uploadPath);
|
||||
apiArg.addProperty("mode", "overwrite");
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://content.dropboxapi.com/2/files/upload"))
|
||||
.header("Authorization", "Bearer " + validToken)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.header("Dropbox-API-Arg", apiArg.toString())
|
||||
.POST(HttpRequest.BodyPublishers.ofFile(resourcePackPath))
|
||||
.build();
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
CraftEngine.instance().logger().info("[Dropbox] Starting upload...");
|
||||
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> {
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
if (response.statusCode() == 200) {
|
||||
CraftEngine.instance().logger().info(
|
||||
"[Dropbox] Upload completed in " + elapsed + "ms");
|
||||
this.url = getDownloadUrl(validToken);
|
||||
saveCacheToDisk();
|
||||
future.complete(null);
|
||||
} else {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[Dropbox] Upload failed (HTTP " + response.statusCode() + "): " + response.body());
|
||||
future.completeExceptionally(new RuntimeException(response.body()));
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
CraftEngine.instance().logger().warn("[Dropbox] Upload error", ex);
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
private String getDownloadUrl(String accessToken) {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
JsonObject requestBody = new JsonObject();
|
||||
requestBody.addProperty("path", this.uploadPath);
|
||||
requestBody.add("settings", new JsonObject());
|
||||
requestBody.getAsJsonObject("settings").addProperty("requested_visibility", "public");
|
||||
|
||||
HttpRequest createLinkRequest = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings"))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(createLinkRequest, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 409) {
|
||||
JsonObject listRequest = new JsonObject();
|
||||
listRequest.addProperty("path", this.uploadPath);
|
||||
|
||||
HttpRequest listLinksRequest = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://api.dropboxapi.com/2/sharing/list_shared_links"))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(listRequest.toString()))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> listResponse = client.send(listLinksRequest, HttpResponse.BodyHandlers.ofString());
|
||||
JsonObject listData = GsonHelper.parseJsonToJsonObject(listResponse.body());
|
||||
JsonArray links = listData.getAsJsonArray("links");
|
||||
if (!links.isEmpty()) {
|
||||
return links.get(0).getAsJsonObject().get("url").getAsString().replace("dl=0", "dl=1");
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject responseData = GsonHelper.parseJsonToJsonObject(response.body());
|
||||
return responseData.get("url").getAsString().replace("dl=0", "dl=1");
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException("Failed to get download URL", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(JsonObject json, String key) {
|
||||
return json.has(key) ? json.get(key).getAsString() : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private long getLong(JsonObject json, String key) {
|
||||
return json.has(key) ? json.get(key).getAsLong() : 0;
|
||||
}
|
||||
|
||||
private String getOrRefreshToken() {
|
||||
if (System.currentTimeMillis() < expiresAt - 30000 && this.accessToken != null) {
|
||||
return this.accessToken;
|
||||
}
|
||||
this.tokenLock.lock();
|
||||
try {
|
||||
if (System.currentTimeMillis() < expiresAt - 30000 && this.accessToken != null) {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
String credentials = this.appKey + ":" + this.appSecret;
|
||||
String authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes());
|
||||
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://alist.nn.ci/tool/dropbox/callback"))
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", authHeader)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(
|
||||
"grant_type=refresh_token" +
|
||||
"&refresh_token=" + this.refreshToken
|
||||
))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
throw new RuntimeException("Refresh failed: " + response.body());
|
||||
}
|
||||
|
||||
JsonObject tokenData = GsonHelper.parseJsonToJsonObject(response.body());
|
||||
this.accessToken = tokenData.get("access_token").getAsString();
|
||||
this.expiresAt = System.currentTimeMillis() +
|
||||
tokenData.get("expires_in").getAsLong() * 1000;
|
||||
|
||||
if (tokenData.has("refresh_token")) {
|
||||
this.refreshToken = tokenData.get("refresh_token").getAsString();
|
||||
}
|
||||
|
||||
saveCacheToDisk();
|
||||
return this.accessToken;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException("Token refresh failed", e);
|
||||
}
|
||||
} finally {
|
||||
this.tokenLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
|
||||
String appKey = useEnv ? System.getenv("CE_DROPBOX_APP_KEY") : (String) arguments.get("app-key");
|
||||
if (appKey == null || appKey.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'app-key' configuration");
|
||||
}
|
||||
String appSecret = useEnv ? System.getenv("CE_DROPBOX_APP_SECRET") : (String) arguments.get("app-secret");
|
||||
if (appSecret == null || appSecret.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'app-secret' configuration");
|
||||
}
|
||||
String refreshToken = useEnv ? System.getenv("CE_DROPBOX_REFRESH_TOKEN") : (String) arguments.get("refresh-token");
|
||||
if (refreshToken == null || refreshToken.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'refresh-token' configuration");
|
||||
}
|
||||
String uploadPath = (String) arguments.getOrDefault("upload-path", "resource_pack.zip");
|
||||
|
||||
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
|
||||
return new DropboxHost(appKey, appSecret, refreshToken, "/" + uploadPath, proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ExternalHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final ResourcePackDownloadData downloadData;
|
||||
|
||||
public ExternalHost(ResourcePackDownloadData downloadData) {
|
||||
this.downloadData = downloadData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
return CompletableFuture.completedFuture(List.of(this.downloadData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.EXTERNAL;
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
String url = (String) arguments.get("url");
|
||||
if (url == null || url.isEmpty()) {
|
||||
throw new IllegalArgumentException("'url' cannot be empty for external host");
|
||||
}
|
||||
String uuid = (String) arguments.get("uuid");
|
||||
if (uuid == null || uuid.isEmpty()) {
|
||||
uuid = UUID.nameUUIDFromBytes(url.getBytes()).toString();
|
||||
}
|
||||
UUID hostUUID = UUID.fromString(uuid);
|
||||
String sha1 = (String) arguments.get("sha1");
|
||||
if (sha1 == null) {
|
||||
sha1 = "";
|
||||
}
|
||||
return new ExternalHost(new ResourcePackDownloadData(url, hostUUID, sha1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class LobFileHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final String apiKey;
|
||||
private final ProxySelector proxy;
|
||||
private AccountInfo accountInfo;
|
||||
|
||||
private String url;
|
||||
private String sha1;
|
||||
private UUID uuid;
|
||||
|
||||
public LobFileHost(String apiKey, ProxySelector proxy) {
|
||||
this.apiKey = apiKey;
|
||||
this.proxy = proxy;
|
||||
this.readCacheFromDisk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.LOBFILE;
|
||||
}
|
||||
|
||||
public void readCacheFromDisk() {
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("lobfile.cache");
|
||||
if (!Files.exists(cachePath)) return;
|
||||
|
||||
try (InputStream is = Files.newInputStream(cachePath)) {
|
||||
Map<String, String> cache = GsonHelper.get().fromJson(
|
||||
new InputStreamReader(is),
|
||||
new TypeToken<Map<String, String>>(){}.getType()
|
||||
);
|
||||
|
||||
this.url = cache.get("url");
|
||||
this.sha1 = cache.get("sha1");
|
||||
|
||||
String uuidString = cache.get("uuid");
|
||||
if (uuidString != null && !uuidString.isEmpty()) {
|
||||
this.uuid = UUID.fromString(uuidString);
|
||||
}
|
||||
|
||||
CraftEngine.instance().logger().info("[LobFile] Loaded cached resource pack info");
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[LobFile] Failed to read cache file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCacheToDisk() {
|
||||
Map<String, String> cache = new HashMap<>();
|
||||
cache.put("url", this.url);
|
||||
cache.put("sha1", this.sha1);
|
||||
cache.put("uuid", this.uuid != null ? this.uuid.toString() : "");
|
||||
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("lobfile.cache");
|
||||
try {
|
||||
Files.writeString(
|
||||
cachePath,
|
||||
GsonHelper.get().toJson(cache),
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[LobFile] Failed to save cache: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getSpaceUsageText() {
|
||||
if (this.accountInfo == null) return "Usage data not available";
|
||||
return String.format("Storage: %d/%d MB (%.1f%% used)",
|
||||
this.accountInfo.getSpaceUsed() / 1_000_000,
|
||||
this.accountInfo.getSpaceQuota() / 1_000_000,
|
||||
(this.accountInfo.getSpaceUsed() * 100.0) / this.accountInfo.getSpaceQuota()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
if (url == null) return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
return CompletableFuture.completedFuture(List.of(ResourcePackDownloadData.of(this.url, this.uuid, this.sha1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
long totalStartTime = System.currentTimeMillis();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try {
|
||||
Map<String, String> hashes = calculateHashes(resourcePackPath);
|
||||
String sha1Hash = hashes.get("SHA-1");
|
||||
String sha256Hash = hashes.get("SHA-256");
|
||||
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
String boundary = UUID.randomUUID().toString();
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://lobfile.com/api/v3/upload.php"))
|
||||
.header("X-API-Key", this.apiKey)
|
||||
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
|
||||
.POST(buildMultipartBody(resourcePackPath, sha256Hash, boundary))
|
||||
.build();
|
||||
|
||||
long uploadStart = System.currentTimeMillis();
|
||||
CraftEngine.instance().logger().info("[LobFile] Starting file upload...");
|
||||
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> {
|
||||
long uploadTime = System.currentTimeMillis() - uploadStart;
|
||||
CraftEngine.instance().logger().info(
|
||||
"[LobFile] Upload request completed in " + uploadTime + "ms");
|
||||
|
||||
handleUploadResponse(response, future, sha1Hash);
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
long totalTime = System.currentTimeMillis() - totalStartTime;
|
||||
CraftEngine.instance().logger().severe(
|
||||
"[LobFile] Upload failed after " + totalTime + "ms", ex);
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
long totalTime = System.currentTimeMillis() - totalStartTime;
|
||||
CraftEngine.instance().logger().severe(
|
||||
"[LobFile] Upload preparation failed after " + totalTime + "ms", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<AccountInfo> fetchAccountInfo() {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://lobfile.com/api/v3/rest/get-account-info"))
|
||||
.header("X-API-Key", this.apiKey)
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenApply(response -> {
|
||||
if (response.statusCode() == 200) {
|
||||
AccountInfo info = GsonHelper.get().fromJson(response.body(), AccountInfo.class);
|
||||
if (info.isSuccess()) {
|
||||
this.accountInfo = info;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Failed to fetch account info: " + response.statusCode());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Map<String, String> calculateHashes(Path path) throws IOException, NoSuchAlgorithmException {
|
||||
Map<String, String> hashes = new HashMap<>();
|
||||
MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
|
||||
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
try (InputStream is = Files.newInputStream(path);
|
||||
DigestInputStream dis = new DigestInputStream(is, sha1Digest)) {
|
||||
DigestInputStream dis2 = new DigestInputStream(dis, sha256Digest);
|
||||
|
||||
while (dis2.read() != -1) ;
|
||||
|
||||
hashes.put("SHA-1", bytesToHex(sha1Digest.digest()));
|
||||
hashes.put("SHA-256", bytesToHex(sha256Digest.digest()));
|
||||
}
|
||||
return hashes;
|
||||
}
|
||||
|
||||
private HttpRequest.BodyPublisher buildMultipartBody(Path filePath, String sha256Hash, String boundary) throws IOException {
|
||||
List<byte[]> parts = new ArrayList<>();
|
||||
String filePartHeader = "--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath.getFileName() + "\"\r\n" +
|
||||
"Content-Type: application/octet-stream\r\n\r\n";
|
||||
parts.add(filePartHeader.getBytes());
|
||||
|
||||
parts.add(Files.readAllBytes(filePath));
|
||||
parts.add("\r\n".getBytes());
|
||||
|
||||
String sha256Part = "--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"sha_256\"\r\n\r\n" +
|
||||
sha256Hash + "\r\n";
|
||||
parts.add(sha256Part.getBytes());
|
||||
|
||||
String endBoundary = "--" + boundary + "--\r\n";
|
||||
parts.add(endBoundary.getBytes());
|
||||
|
||||
return HttpRequest.BodyPublishers.ofByteArrays(parts);
|
||||
}
|
||||
|
||||
private void handleUploadResponse(
|
||||
HttpResponse<String> response,
|
||||
CompletableFuture<Void> future,
|
||||
String localSha1
|
||||
) {
|
||||
try {
|
||||
if (response.statusCode() == 200) {
|
||||
Map<String, Object> json = GsonHelper.parseJsonToMap(response.body());
|
||||
if (Boolean.TRUE.equals(json.get("success"))) {
|
||||
this.url = (String) json.get("url");
|
||||
this.sha1 = localSha1;
|
||||
this.uuid = UUID.randomUUID();
|
||||
saveCacheToDisk();
|
||||
CraftEngine.instance().logger().info("[LobFile] Upload success! Resource pack URL: " + this.url);
|
||||
fetchAccountInfo()
|
||||
.thenAccept(info -> {
|
||||
CraftEngine.instance().logger().info("[LobFile] Account usage updated: " + getSpaceUsageText());
|
||||
future.complete(null);
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
CraftEngine.instance().logger().warn("[LobFile] Usage check failed (upload still succeeded): ", ex);
|
||||
future.complete(null);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
future.completeExceptionally(new RuntimeException((String) json.get("error")));
|
||||
}
|
||||
} else {
|
||||
future.completeExceptionally(new RuntimeException("Upload failed: " + response.statusCode()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String bytesToHex(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
|
||||
String apiKey = useEnv ? System.getenv("CE_LOBFILE_API_KEY") : (String) arguments.get("api-key");
|
||||
if (apiKey == null || apiKey.isEmpty()) {
|
||||
throw new RuntimeException("Missing 'api-key' for LobFileHost");
|
||||
}
|
||||
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
|
||||
return new LobFileHost(apiKey, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"all"})
|
||||
public static class AccountInfo {
|
||||
private boolean success;
|
||||
private Map<String, Object> account_info;
|
||||
private Map<String, Integer> account_limits;
|
||||
private Map<String, Integer> account_usage;
|
||||
|
||||
public String getEmail() {
|
||||
return (String) this.account_info.get("email");
|
||||
}
|
||||
|
||||
public int getSpaceQuota() {
|
||||
return this.account_limits.getOrDefault("space_quota", 0);
|
||||
}
|
||||
|
||||
public int getSpaceUsed() {
|
||||
return this.account_usage.getOrDefault("space_used", 0);
|
||||
}
|
||||
|
||||
public int getSlotsUsed() {
|
||||
return this.account_usage.getOrDefault("slots_used", 0);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return this.success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class NoneHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
public static final NoneHost INSTANCE = new NoneHost();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
return CompletableFuture.completedFuture(List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.NONE;
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class OneDriveHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final String clientId;
|
||||
private final String clientSecret;
|
||||
private final ProxySelector proxy;
|
||||
private final String uploadPath;
|
||||
private Tuple<String, String, Date> refreshToken;
|
||||
private String sha1;
|
||||
private String fileId;
|
||||
|
||||
public OneDriveHost(String clientId,
|
||||
String clientSecret,
|
||||
String refreshToken,
|
||||
String uploadPath,
|
||||
ProxySelector proxy) {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.proxy = proxy;
|
||||
this.uploadPath = uploadPath;
|
||||
this.refreshToken = Tuple.of(refreshToken, "", new Date());
|
||||
readCacheFromDisk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.ONEDRIVE;
|
||||
}
|
||||
|
||||
public void readCacheFromDisk() {
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("onedrive.cache");
|
||||
if (!Files.exists(cachePath)) return;
|
||||
|
||||
try (InputStream is = Files.newInputStream(cachePath)) {
|
||||
Map<String, String> cache = GsonHelper.get().fromJson(
|
||||
new InputStreamReader(is),
|
||||
new TypeToken<Map<String, String>>(){}.getType()
|
||||
);
|
||||
|
||||
this.refreshToken = Tuple.of(
|
||||
cache.get("refresh-token"),
|
||||
cache.get("access-token"),
|
||||
new Date(Long.parseLong(cache.get("refresh-token-expires-in"))));
|
||||
this.sha1 = cache.get("sha1");
|
||||
this.fileId = cache.get("file-id");
|
||||
|
||||
CraftEngine.instance().logger().info("[OneDrive] Loaded cached resource pack info");
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[OneDrive] Failed to load cache from disk: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCacheToDisk() {
|
||||
Map<String, String> cache = new HashMap<>();
|
||||
cache.put("refresh-token", this.refreshToken.left());
|
||||
cache.put("access-token", this.refreshToken.mid());
|
||||
cache.put("refresh-token-expires-in", String.valueOf(this.refreshToken.right().getTime()));
|
||||
cache.put("sha1", this.sha1);
|
||||
cache.put("file-id", this.fileId);
|
||||
|
||||
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("onedrive.cache");
|
||||
try {
|
||||
Files.writeString(
|
||||
cachePath,
|
||||
GsonHelper.get().toJson(cache),
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[OneDrive] Failed to persist cache to disk: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
CompletableFuture<List<ResourcePackDownloadData>> future = new CompletableFuture<>();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
String accessToken = getOrRefreshJwtToken();
|
||||
saveCacheToDisk();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://graph.microsoft.com/v1.0/drive/items/" + this.fileId))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.GET()
|
||||
.build();
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> {
|
||||
if (response.statusCode() != 200) {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Failed to retrieve download URL (HTTP " + response.statusCode() + "): " + response.body());
|
||||
future.completeExceptionally(new IOException("HTTP " + response.statusCode() + ": " + response.body()));
|
||||
return;
|
||||
}
|
||||
String downloadUrl = GsonHelper.parseJsonToJsonObject(response.body()).get("@microsoft.graph.downloadUrl").getAsString();
|
||||
future.complete(List.of(new ResourcePackDownloadData(
|
||||
downloadUrl,
|
||||
UUID.nameUUIDFromBytes(this.sha1.getBytes(StandardCharsets.UTF_8)),
|
||||
this.sha1
|
||||
)));
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Error retrieving download link: " + ex.getMessage());
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
this.sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
|
||||
String accessToken = getOrRefreshJwtToken();
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://graph.microsoft.com/v1.0/drive/root:/" + this.uploadPath + ":/content"))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofFile(resourcePackPath))
|
||||
.build();
|
||||
long uploadStart = System.currentTimeMillis();
|
||||
CraftEngine.instance().logger().info("[OneDrive] Initiating resource pack upload...");
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||
.thenAccept(response -> {
|
||||
long elapsedTime = System.currentTimeMillis() - uploadStart;
|
||||
if (response.statusCode() == 200 || response.statusCode() == 201) {
|
||||
CraftEngine.instance().logger().info("[OneDrive] Successfully uploaded resource pack in " + elapsedTime + " ms");
|
||||
this.fileId = GsonHelper.parseJsonToJsonObject(response.body()).get("id").getAsString();
|
||||
saveCacheToDisk();
|
||||
future.complete(null);
|
||||
} else {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Upload failed (HTTP " + response.statusCode() + "): " + response.body());
|
||||
future.completeExceptionally(new RuntimeException("HTTP " + response.statusCode() + ": " + response.body()));
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Upload operation failed: " + ex.getMessage());
|
||||
future.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
} catch (FileNotFoundException e) {
|
||||
CraftEngine.instance().logger().warn("[OneDrive] Resource pack file not found: " + e.getMessage());
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private String getOrRefreshJwtToken() {
|
||||
if (this.refreshToken == null || this.refreshToken.mid().isEmpty() || this.refreshToken.right().before(new Date())) {
|
||||
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
|
||||
String formData = "client_id=" + URLEncoder.encode(this.clientId, StandardCharsets.UTF_8) +
|
||||
"&client_secret=" + URLEncoder.encode(this.clientSecret, StandardCharsets.UTF_8) +
|
||||
"&redirect_uri=" + URLEncoder.encode("https://alist.nn.ci/tool/onedrive/callback", StandardCharsets.UTF_8) +
|
||||
"&refresh_token=" + URLEncoder.encode(this.refreshToken.left(), StandardCharsets.UTF_8) +
|
||||
"&grant_type=refresh_token" +
|
||||
"&scope=Files.ReadWrite.All+offline_access";
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://login.microsoftonline.com/common/oauth2/v2.0/token"))
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(formData))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Authentication failed (HTTP " + response.statusCode() + "): " + response.body());
|
||||
return this.refreshToken != null ? this.refreshToken.mid() : "";
|
||||
}
|
||||
|
||||
JsonObject jsonData = GsonHelper.parseJsonToJsonObject(response.body());
|
||||
if (jsonData.has("error")) {
|
||||
CraftEngine.instance().logger().warn("[OneDrive] Token refresh error: " + jsonData);
|
||||
throw new RuntimeException("Authentication error: " + jsonData);
|
||||
}
|
||||
long expiresInMillis = jsonData.get("expires_in").getAsInt() * 1000L;
|
||||
this.refreshToken = Tuple.of(
|
||||
jsonData.get("refresh_token").getAsString(),
|
||||
jsonData.get("access_token").getAsString(),
|
||||
new Date(System.currentTimeMillis() + expiresInMillis - 10_000)
|
||||
);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
CraftEngine.instance().logger().severe("[OneDrive] Token refresh failure: " + e.getMessage());
|
||||
throw new RuntimeException("Authentication process failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
return this.refreshToken.mid();
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
|
||||
String clientId = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_ID") : (String) arguments.get("client-id");
|
||||
if (clientId == null || clientId.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'client-id' configuration");
|
||||
}
|
||||
String clientSecret = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_SECRET") : (String) arguments.get("client-secret");
|
||||
if (clientSecret == null || clientSecret.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'client-secret' configuration");
|
||||
}
|
||||
String refreshToken = useEnv ? System.getenv("CE_ONEDRIVE_REFRESH_TOKEN") : (String) arguments.get("refresh-token");
|
||||
if (refreshToken == null || refreshToken.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing required 'refresh-token' configuration");
|
||||
}
|
||||
String uploadPath = (String) arguments.getOrDefault("upload-path", "resource_pack.zip");
|
||||
if (uploadPath == null || uploadPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid 'upload-path' configuration");
|
||||
}
|
||||
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
|
||||
return new OneDriveHost(clientId, clientSecret, refreshToken, uploadPath, proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.HashUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
|
||||
import software.amazon.awssdk.core.async.AsyncRequestBody;
|
||||
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
|
||||
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
|
||||
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
|
||||
import software.amazon.awssdk.http.nio.netty.ProxyConfiguration;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3AsyncClient;
|
||||
import software.amazon.awssdk.services.s3.S3AsyncClientBuilder;
|
||||
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class S3Host implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final S3AsyncClient s3AsyncClient;
|
||||
private final S3Presigner preSigner;
|
||||
private final String bucket;
|
||||
private final String uploadPath;
|
||||
private final String cdnDomain;
|
||||
private final String cdnProtocol;
|
||||
private final Duration validity;
|
||||
|
||||
public S3Host(
|
||||
S3AsyncClient s3AsyncClient,
|
||||
S3Presigner preSigner,
|
||||
String bucket,
|
||||
String uploadPath,
|
||||
String cdnDomain,
|
||||
String cdnProtocol,
|
||||
Duration validity
|
||||
) {
|
||||
this.s3AsyncClient = s3AsyncClient;
|
||||
this.preSigner = preSigner;
|
||||
this.bucket = bucket;
|
||||
this.uploadPath = uploadPath;
|
||||
this.cdnDomain = cdnDomain;
|
||||
this.cdnProtocol = cdnProtocol;
|
||||
this.validity = validity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.S3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
return this.s3AsyncClient.headObject(HeadObjectRequest.builder()
|
||||
.bucket(this.bucket)
|
||||
.key(this.uploadPath)
|
||||
.build())
|
||||
.handle((headResponse, exception) -> {
|
||||
if (exception != null) {
|
||||
Throwable cause = exception.getCause();
|
||||
if (cause instanceof NoSuchKeyException) {
|
||||
CraftEngine.instance().logger().warn("[S3] Resource pack not found in bucket '" + this.bucket + "'. Path: " + this.uploadPath);
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[S3] Failed to retrieve resource pack metadata. Reason: " +
|
||||
cause.getClass().getSimpleName() + " - " + cause.getMessage()
|
||||
);
|
||||
throw new CompletionException("Metadata request failed for path: " + this.uploadPath, cause);
|
||||
}
|
||||
}
|
||||
String sha1 = headResponse.metadata().get("sha1");
|
||||
if (sha1 == null) {
|
||||
CraftEngine.instance().logger().warn("[S3] Missing SHA-1 checksum in object metadata. Path: " + this.uploadPath);
|
||||
throw new CompletionException(new IllegalStateException("Missing SHA-1 metadata for S3 object: " + this.uploadPath));
|
||||
}
|
||||
GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
|
||||
.signatureDuration(this.validity)
|
||||
.getObjectRequest(b -> b.bucket(this.bucket).key(this.uploadPath))
|
||||
.build();
|
||||
return Collections.singletonList(
|
||||
ResourcePackDownloadData.of(
|
||||
replaceWithCdnUrl(this.preSigner.presignGetObject(presignRequest).url()),
|
||||
UUID.nameUUIDFromBytes(sha1.getBytes(StandardCharsets.UTF_8)),
|
||||
sha1
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
String sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
|
||||
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
||||
.bucket(this.bucket)
|
||||
.key(this.uploadPath)
|
||||
.metadata(Map.of("sha1", sha1))
|
||||
.build();
|
||||
long uploadStart = System.currentTimeMillis();
|
||||
CraftEngine.instance().logger().info("[S3] Initiating resource pack upload to '" + this.uploadPath + "'");
|
||||
return this.s3AsyncClient.putObject(putObjectRequest, AsyncRequestBody.fromFile(resourcePackPath))
|
||||
.handle((response, exception) -> {
|
||||
if (exception != null) {
|
||||
Throwable cause = exception instanceof CompletionException ?
|
||||
exception.getCause() :
|
||||
exception;
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[S3] Upload failed for path '" + this.uploadPath + "'. Error: " +
|
||||
cause.getClass().getSimpleName() + " - " + cause.getMessage()
|
||||
);
|
||||
throw new CompletionException("Failed to upload to S3 path: " + this.uploadPath, cause);
|
||||
}
|
||||
CraftEngine.instance().logger().info(
|
||||
"[S3] Successfully uploaded resource pack to '" + this.uploadPath + "' in " +
|
||||
(System.currentTimeMillis() - uploadStart) + " ms"
|
||||
);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private String replaceWithCdnUrl(URL originalUrl) {
|
||||
if (this.cdnDomain == null) return originalUrl.toString();
|
||||
return this.cdnProtocol + "://" + this.cdnDomain
|
||||
+ originalUrl.getPath()
|
||||
+ (originalUrl.getQuery() != null ? "?" + originalUrl.getQuery() : "");
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
|
||||
String endpoint = (String) arguments.get("endpoint");
|
||||
if (endpoint == null || endpoint.isEmpty()) {
|
||||
throw new IllegalArgumentException("'endpoint' cannot be empty for S3 host");
|
||||
}
|
||||
String protocol = (String) arguments.getOrDefault("protocol", "https");
|
||||
boolean usePathStyle = (boolean) arguments.getOrDefault("path-style", false);
|
||||
String bucket = (String) arguments.get("bucket");
|
||||
if (bucket == null || bucket.isEmpty()) {
|
||||
throw new IllegalArgumentException("'bucket' cannot be empty for S3 host");
|
||||
}
|
||||
String region = (String) arguments.getOrDefault("region", "auto");
|
||||
String accessKeyId = useEnv ? System.getenv("CE_S3_ACCESS_KEY_ID") : (String) arguments.get("access-key-id");
|
||||
if (accessKeyId == null || accessKeyId.isEmpty()) {
|
||||
throw new IllegalArgumentException("'access-key-id' cannot be empty for S3 host");
|
||||
}
|
||||
String accessKeySecret = useEnv ? System.getenv("CE_S3_ACCESS_KEY_SECRET") : (String) arguments.get("access-key-secret");
|
||||
if (accessKeySecret == null || accessKeySecret.isEmpty()) {
|
||||
throw new IllegalArgumentException("'access-key-secret' cannot be empty for S3 host");
|
||||
}
|
||||
String uploadPath = (String) arguments.getOrDefault("upload-path", "craftengine/resource_pack.zip");
|
||||
if (uploadPath == null || uploadPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("'upload-path' cannot be empty for S3 host");
|
||||
}
|
||||
boolean useLegacySignature = (boolean) arguments.getOrDefault("use-legacy-signature", true);
|
||||
Duration validity = Duration.ofSeconds((int) arguments.getOrDefault("validity", 10));
|
||||
|
||||
Map<String, Object> cdn = MiscUtils.castToMap(arguments.get("cdn"), true);
|
||||
String cdnDomain = null;
|
||||
String cdnProtocol = "https";
|
||||
if (cdn != null) {
|
||||
cdnDomain = (String) cdn.get("domain");
|
||||
cdnProtocol = (String) cdn.getOrDefault("protocol", "https");
|
||||
}
|
||||
|
||||
AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKeyId, accessKeySecret);
|
||||
|
||||
S3AsyncClientBuilder s3AsyncClientBuilder = S3AsyncClient.builder()
|
||||
.endpointOverride(URI.create(protocol + "://" + endpoint))
|
||||
.region(Region.of(region))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.serviceConfiguration(b -> b.pathStyleAccessEnabled(usePathStyle));
|
||||
|
||||
if (useLegacySignature) {
|
||||
s3AsyncClientBuilder.overrideConfiguration(b -> b
|
||||
.putAdvancedOption(SdkAdvancedClientOption.SIGNER, AwsS3V4Signer.create())
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Object> proxySetting = MiscUtils.castToMap(arguments.get("proxy"), true);
|
||||
if (proxySetting != null) {
|
||||
String host = (String) proxySetting.get("host");
|
||||
int port = (Integer) proxySetting.get("port");
|
||||
String scheme = (String) proxySetting.get("scheme");
|
||||
String username = (String) proxySetting.get("username");
|
||||
String password = (String) proxySetting.get("password");
|
||||
if (host == null || host.isEmpty() || port <= 0 || port > 65535 || scheme == null || scheme.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid proxy configuration");
|
||||
}
|
||||
ProxyConfiguration.Builder builder = ProxyConfiguration.builder().host(host).port(port).scheme(scheme);
|
||||
if (username != null) builder.username(username);
|
||||
if (password != null) builder.password(password);
|
||||
SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder().proxyConfiguration(builder.build()).build();
|
||||
s3AsyncClientBuilder.httpClient(httpClient);
|
||||
}
|
||||
|
||||
S3AsyncClient s3AsyncClient = s3AsyncClientBuilder.build();
|
||||
|
||||
S3Presigner preSigner = S3Presigner.builder()
|
||||
.endpointOverride(URI.create(protocol + "://" + endpoint))
|
||||
.region(Region.of(region))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
return new S3Host(s3AsyncClient, preSigner, bucket, uploadPath, cdnDomain, cdnProtocol, validity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SelfHost implements ResourcePackHost {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private static final SelfHost INSTANCE = new SelfHost();
|
||||
|
||||
public SelfHost() {
|
||||
SelfHostHttpServer.instance().readResourcePack(Config.fileToUpload());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
|
||||
ResourcePackDownloadData data = SelfHostHttpServer.instance().generateOneTimeUrl();
|
||||
if (data == null) return CompletableFuture.completedFuture(List.of());
|
||||
return CompletableFuture.completedFuture(List.of(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> upload(Path resourcePackPath) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
CraftEngine.instance().scheduler().executeAsync(() -> {
|
||||
try {
|
||||
SelfHostHttpServer.instance().readResourcePack(resourcePackPath);
|
||||
future.complete(null);
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return ResourcePackHosts.SELF;
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackHostFactory {
|
||||
|
||||
@Override
|
||||
public ResourcePackHost create(Map<String, Object> arguments) {
|
||||
SelfHostHttpServer selfHostHttpServer = SelfHostHttpServer.instance();
|
||||
String ip = (String) arguments.get("ip");
|
||||
if (ip == null) {
|
||||
throw new IllegalArgumentException("'ip' argument missing for self host");
|
||||
}
|
||||
int port = (int) arguments.get("port");
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("Illegal port: '" + port + "' for self host");
|
||||
}
|
||||
boolean oneTimeToken = (boolean) arguments.getOrDefault("one-time-token", true);
|
||||
String protocol = (String) arguments.getOrDefault("protocol", "http");
|
||||
boolean denyNonMinecraftRequest = (boolean) arguments.getOrDefault("deny-non-minecraft-request", true);
|
||||
Map<String, Object> rateMap = MiscUtils.castToMap(arguments.get("rate-map"), true);
|
||||
int maxRequests = 5;
|
||||
int resetInterval = 20_000;
|
||||
if (rateMap != null) {
|
||||
maxRequests = (int) rateMap.getOrDefault("max-requests", 5);
|
||||
resetInterval = (int) rateMap.getOrDefault("reset-interval", 20) * 1000;
|
||||
}
|
||||
selfHostHttpServer.updateProperties(ip, port, denyNonMinecraftRequest, protocol, maxRequests, resetInterval, oneTimeToken);
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package net.momirealms.craftengine.core.pack.host.impl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class SelfHostHttpServer {
|
||||
private static SelfHostHttpServer instance;
|
||||
private final Cache<String, Boolean> oneTimePackUrls = Caffeine.newBuilder()
|
||||
.maximumSize(256)
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
private final Cache<String, IpAccessRecord> ipAccessCache = Caffeine.newBuilder()
|
||||
.maximumSize(256)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private ExecutorService threadPool;
|
||||
private HttpServer server;
|
||||
|
||||
private final AtomicLong totalRequests = new AtomicLong();
|
||||
private final AtomicLong blockedRequests = new AtomicLong();
|
||||
|
||||
private int rateLimit = 1;
|
||||
private long rateLimitInterval = 1000;
|
||||
private String ip = "localhost";
|
||||
private int port = -1;
|
||||
private String protocol = "http";
|
||||
private boolean denyNonMinecraft = true;
|
||||
private boolean useToken;
|
||||
|
||||
private volatile byte[] resourcePackBytes;
|
||||
private String packHash;
|
||||
private UUID packUUID;
|
||||
|
||||
public void updateProperties(String ip,
|
||||
int port,
|
||||
boolean denyNonMinecraft,
|
||||
String protocol,
|
||||
int maxRequests,
|
||||
int resetInternal,
|
||||
boolean token) {
|
||||
this.ip = ip;
|
||||
this.denyNonMinecraft = denyNonMinecraft;
|
||||
this.protocol = protocol;
|
||||
this.rateLimit = maxRequests;
|
||||
this.rateLimitInterval = resetInternal;
|
||||
this.useToken = token;
|
||||
if (port <= 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("Invalid port number: " + port);
|
||||
}
|
||||
if (port == this.port && this.server != null) return;
|
||||
if (this.server != null) disable();
|
||||
this.port = port;
|
||||
try {
|
||||
this.threadPool = Executors.newFixedThreadPool(1);
|
||||
this.server = HttpServer.create(new InetSocketAddress("::", port), 0);
|
||||
this.server.createContext("/download", new ResourcePackHandler());
|
||||
this.server.createContext("/metrics", this::handleMetrics);
|
||||
this.server.setExecutor(this.threadPool);
|
||||
this.server.start();
|
||||
CraftEngine.instance().logger().info("HTTP server started on port: " + port);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to start HTTP server", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static SelfHostHttpServer instance() {
|
||||
if (instance == null) {
|
||||
instance = new SelfHostHttpServer();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResourcePackDownloadData generateOneTimeUrl() {
|
||||
if (this.resourcePackBytes == null) {
|
||||
return null;
|
||||
}
|
||||
if (!this.useToken) {
|
||||
return new ResourcePackDownloadData(url() + "download", this.packUUID, this.packHash);
|
||||
}
|
||||
String token = UUID.randomUUID().toString();
|
||||
this.oneTimePackUrls.put(token, true);
|
||||
return new ResourcePackDownloadData(
|
||||
url() + "download?token=" + URLEncoder.encode(token, StandardCharsets.UTF_8),
|
||||
this.packUUID,
|
||||
this.packHash
|
||||
);
|
||||
}
|
||||
|
||||
public String url() {
|
||||
return this.protocol + "://" + this.ip + ":" + this.port + "/";
|
||||
}
|
||||
|
||||
public void readResourcePack(Path path) {
|
||||
try {
|
||||
if (Files.exists(path)) {
|
||||
this.resourcePackBytes = Files.readAllBytes(path);
|
||||
calculateHash();
|
||||
} else {
|
||||
this.resourcePackBytes = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().severe("Failed to load resource pack", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateHash() {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
digest.update(this.resourcePackBytes);
|
||||
byte[] hashBytes = digest.digest();
|
||||
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hashBytes) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
this.packHash = hexString.toString();
|
||||
this.packUUID = UUID.nameUUIDFromBytes(this.packHash.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
CraftEngine.instance().logger().severe("SHA-1 algorithm not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMetrics(HttpExchange exchange) throws IOException {
|
||||
String metrics = "# TYPE total_requests counter\n"
|
||||
+ "total_requests " + totalRequests.get() + "\n"
|
||||
+ "# TYPE blocked_requests counter\n"
|
||||
+ "blocked_requests " + blockedRequests.get();
|
||||
|
||||
exchange.getResponseHeaders().set("Content-Type", "text/plain");
|
||||
exchange.sendResponseHeaders(200, metrics.length());
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(metrics.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
if (this.server != null) {
|
||||
this.server.stop(0);
|
||||
this.server = null;
|
||||
if (this.threadPool != null) {
|
||||
this.threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ResourcePackHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
totalRequests.incrementAndGet();
|
||||
|
||||
String clientIp = getClientIp(exchange);
|
||||
if (checkRateLimit(clientIp)) {
|
||||
handleBlockedRequest(exchange, 429, "Rate limit exceeded");
|
||||
return;
|
||||
}
|
||||
if (useToken) {
|
||||
String token = parseToken(exchange);
|
||||
if (!validateToken(token)) {
|
||||
handleBlockedRequest(exchange, 403, "Invalid token");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!validateClient(exchange)) {
|
||||
handleBlockedRequest(exchange, 403, "Invalid client");
|
||||
return;
|
||||
}
|
||||
if (resourcePackBytes == null) {
|
||||
handleBlockedRequest(exchange, 404, "Resource pack missing");
|
||||
return;
|
||||
}
|
||||
sendResourcePack(exchange);
|
||||
}
|
||||
|
||||
private String getClientIp(HttpExchange exchange) {
|
||||
return exchange.getRemoteAddress().getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
private boolean checkRateLimit(String clientIp) {
|
||||
IpAccessRecord record = ipAccessCache.getIfPresent(clientIp);
|
||||
long now = System.currentTimeMillis();
|
||||
if (record == null) {
|
||||
record = new IpAccessRecord(now, 1);
|
||||
ipAccessCache.put(clientIp, record);
|
||||
} else {
|
||||
if (now - record.lastAccessTime > rateLimitInterval) {
|
||||
record = new IpAccessRecord(now, 1);
|
||||
ipAccessCache.put(clientIp, record);
|
||||
} else {
|
||||
record.accessCount++;
|
||||
}
|
||||
}
|
||||
return record.accessCount > rateLimit;
|
||||
}
|
||||
|
||||
private String parseToken(HttpExchange exchange) {
|
||||
Map<String, String> params = parseQuery(exchange.getRequestURI().getQuery());
|
||||
return params.get("token");
|
||||
}
|
||||
|
||||
private boolean validateToken(String token) {
|
||||
if (token == null || token.length() != 36) return false;
|
||||
|
||||
Boolean valid = oneTimePackUrls.getIfPresent(token);
|
||||
if (valid != null) {
|
||||
oneTimePackUrls.invalidate(token);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean validateClient(HttpExchange exchange) {
|
||||
if (!denyNonMinecraft) return true;
|
||||
|
||||
String userAgent = exchange.getRequestHeaders().getFirst("User-Agent");
|
||||
return userAgent != null && userAgent.startsWith("Minecraft Java/");
|
||||
}
|
||||
|
||||
private void sendResourcePack(HttpExchange exchange) throws IOException {
|
||||
exchange.getResponseHeaders().set("Content-Type", "application/zip");
|
||||
exchange.getResponseHeaders().set("Content-Length", String.valueOf(resourcePackBytes.length));
|
||||
exchange.sendResponseHeaders(200, resourcePackBytes.length);
|
||||
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(resourcePackBytes);
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to send resource pack", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBlockedRequest(HttpExchange exchange, int code, String reason) throws IOException {
|
||||
blockedRequests.incrementAndGet();
|
||||
CraftEngine.instance().debug(() ->
|
||||
String.format("Blocked request [%s] %s: %s",
|
||||
code,
|
||||
exchange.getRemoteAddress(),
|
||||
reason)
|
||||
);
|
||||
exchange.sendResponseHeaders(code, -1);
|
||||
exchange.close();
|
||||
}
|
||||
|
||||
private Map<String, String> parseQuery(String query) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if (query == null) return params;
|
||||
|
||||
for (String pair : query.split("&")) {
|
||||
int idx = pair.indexOf("=");
|
||||
String key = idx > 0 ? pair.substring(0, idx) : pair;
|
||||
String value = idx > 0 ? pair.substring(idx + 1) : "";
|
||||
params.put(key, value);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
private static class IpAccessRecord {
|
||||
final long lastAccessTime;
|
||||
int accessCount;
|
||||
|
||||
IpAccessRecord(long lastAccessTime, int accessCount) {
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
this.accessCount = accessCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.core.pack;
|
||||
package net.momirealms.craftengine.core.pack.model;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -130,6 +130,7 @@ public final class ObfD {
|
||||
long 原始大小;
|
||||
byte[] 编码路径;
|
||||
int 压缩方法;
|
||||
boolean 兄弟别搞;
|
||||
}
|
||||
|
||||
private static class 文件条目注册表<E> extends ArrayList<E> {
|
||||
@@ -201,6 +202,7 @@ public final class ObfD {
|
||||
描述.存储偏移 = 上下文.获取当前偏移();
|
||||
描述.编码路径 = 虚拟路径.getBytes(StandardCharsets.UTF_8);
|
||||
描述.压缩方法 = 结果.大小减少 ? Deflater.DEFLATED : Deflater.NO_COMPRESSION;
|
||||
描述.兄弟别搞 = (虚拟路径.getBytes(StandardCharsets.UTF_8).length >= 0xFFFF);
|
||||
}
|
||||
|
||||
private static void 完成压缩包结构(压缩元数据写入器 上下文,
|
||||
@@ -221,6 +223,7 @@ public final class ObfD {
|
||||
|
||||
private static void 写入中央目录条目(压缩元数据写入器 上下文,
|
||||
文件条目描述 条目) throws IOException {
|
||||
if(条目.兄弟别搞) return;
|
||||
写入签名头(上下文, 压缩头验证器.中央目录标记);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeManager;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLootManager;
|
||||
import net.momirealms.craftengine.core.pack.PackManager;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
|
||||
@@ -70,6 +69,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
|
||||
private final Consumer<CraftEngine> reloadEventDispatcher;
|
||||
private boolean isReloading;
|
||||
private boolean isInitializing;
|
||||
|
||||
private String buildByBit = "%%__BUILTBYBIT__%%";
|
||||
private String polymart = "%%__POLYMART__%%";
|
||||
@@ -192,6 +192,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
}
|
||||
|
||||
public void onPluginEnable() {
|
||||
this.isInitializing = true;
|
||||
this.networkManager.init();
|
||||
this.templateManager = new TemplateManagerImpl();
|
||||
this.itemBrowserManager = new ItemBrowserManagerImpl(this);
|
||||
@@ -220,6 +221,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.furnitureManager.delayedInit();
|
||||
// set up some platform extra tasks
|
||||
this.platformDelayedEnable();
|
||||
this.isInitializing = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,7 +245,6 @@ public abstract class CraftEngine implements Plugin {
|
||||
if (this.commandManager != null) this.commandManager.unregisterFeatures();
|
||||
if (this.senderFactory != null) this.senderFactory.close();
|
||||
if (this.dependencyManager != null) this.dependencyManager.close();
|
||||
ResourcePackHost.instance().disable();
|
||||
}
|
||||
|
||||
protected void registerDefaultParsers() {
|
||||
@@ -278,7 +279,6 @@ public abstract class CraftEngine implements Plugin {
|
||||
Dependencies.BSTATS_BASE,
|
||||
Dependencies.CAFFEINE,
|
||||
Dependencies.GEANTY_REF,
|
||||
Dependencies.NETTY_HTTP,
|
||||
Dependencies.CLOUD_CORE, Dependencies.CLOUD_SERVICES,
|
||||
Dependencies.GSON,
|
||||
Dependencies.SLF4J_API, Dependencies.SLF4J_SIMPLE,
|
||||
@@ -291,7 +291,39 @@ public abstract class CraftEngine implements Plugin {
|
||||
Dependencies.TEXT_SERIALIZER_GSON, Dependencies.TEXT_SERIALIZER_GSON_LEGACY,
|
||||
Dependencies.TEXT_SERIALIZER_JSON,
|
||||
Dependencies.AHO_CORASICK,
|
||||
Dependencies.LZ4
|
||||
Dependencies.LZ4,
|
||||
Dependencies.NETTY_HTTP,
|
||||
Dependencies.NETTY_HTTP2,
|
||||
Dependencies.REACTIVE_STREAMS,
|
||||
Dependencies.AMAZON_AWSSDK_S3,
|
||||
Dependencies.AMAZON_AWSSDK_NETTY_NIO_CLIENT,
|
||||
Dependencies.AMAZON_AWSSDK_SDK_CORE,
|
||||
Dependencies.AMAZON_AWSSDK_AUTH,
|
||||
Dependencies.AMAZON_AWSSDK_REGIONS,
|
||||
Dependencies.AMAZON_AWSSDK_IDENTITY_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_HTTP_CLIENT_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_PROTOCOL_CORE,
|
||||
Dependencies.AMAZON_AWSSDK_AWS_XML_PROTOCOL,
|
||||
Dependencies.AMAZON_AWSSDK_JSON_UTILS,
|
||||
Dependencies.AMAZON_AWSSDK_AWS_CORE,
|
||||
Dependencies.AMAZON_AWSSDK_UTILS,
|
||||
Dependencies.AMAZON_AWSSDK_ANNOTATIONS,
|
||||
Dependencies.AMAZON_AWSSDK_CRT_CORE,
|
||||
Dependencies.AMAZON_AWSSDK_CHECKSUMS,
|
||||
Dependencies.AMAZON_EVENTSTREAM,
|
||||
Dependencies.AMAZON_AWSSDK_PROFILES,
|
||||
Dependencies.AMAZON_AWSSDK_RETRIES,
|
||||
Dependencies.AMAZON_AWSSDK_ENDPOINTS_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_ARNS,
|
||||
Dependencies.AMAZON_AWSSDK_AWS_QUERY_PROTOCOL,
|
||||
Dependencies.AMAZON_AWSSDK_HTTP_AUTH_AWS,
|
||||
Dependencies.AMAZON_AWSSDK_HTTP_AUTH_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_HTTP_AUTH,
|
||||
Dependencies.AMAZON_AWSSDK_HTTP_AUTH_AWS_EVENTSTREAM,
|
||||
Dependencies.AMAZON_AWSSDK_CHECKSUMS_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_RETRIES_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_METRICS_SPI,
|
||||
Dependencies.AMAZON_AWSSDK_THIRD_PARTY_JACKSON_CORE
|
||||
);
|
||||
}
|
||||
|
||||
@@ -326,6 +358,11 @@ public abstract class CraftEngine implements Plugin {
|
||||
return isReloading;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitializing() {
|
||||
return isInitializing;
|
||||
}
|
||||
|
||||
public abstract boolean hasPlaceholderAPI();
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,8 @@ public interface Plugin {
|
||||
|
||||
boolean isReloading();
|
||||
|
||||
boolean isInitializing();
|
||||
|
||||
DependencyManager dependencyManager();
|
||||
|
||||
<W> SchedulerAdapter<W> scheduler();
|
||||
|
||||
@@ -13,7 +13,6 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
|
||||
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.core.pack.conflict.resolution.ConditionalResolution;
|
||||
import net.momirealms.craftengine.core.pack.host.HostMode;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.PluginProperties;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
@@ -30,10 +29,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Config {
|
||||
@@ -79,24 +79,13 @@ public class Config {
|
||||
protected float resource_pack$supported_version$min;
|
||||
protected float resource_pack$supported_version$max;
|
||||
|
||||
protected HostMode resource_pack$send$mode;
|
||||
protected boolean resource_pack$send$kick_if_declined;
|
||||
protected boolean resource_pack$send$send_on_join;
|
||||
protected boolean resource_pack$send$send_on_reload;
|
||||
protected boolean resource_pack$delivery$kick_if_declined;
|
||||
protected boolean resource_pack$delivery$send_on_join;
|
||||
protected boolean resource_pack$delivery$resend_on_upload;
|
||||
protected boolean resource_pack$delivery$auto_upload;
|
||||
protected Path resource_pack$delivery$file_to_upload;
|
||||
protected Component resource_pack$send$prompt;
|
||||
|
||||
protected int resource_pack$send$self_host$port;
|
||||
protected String resource_pack$send$self_host$ip;
|
||||
protected String resource_pack$send$self_host$protocol;
|
||||
protected boolean resource_pack$send$self_host$deny_non_minecraft_request;
|
||||
protected int resource_pack$send$self_host$rate_limit$max_requests;
|
||||
protected long resource_pack$send$self_host$rate_limit$reset_interval;
|
||||
protected String resource_pack$self_host$local_file_path;
|
||||
|
||||
protected String resource_pack$external_host$url;
|
||||
protected String resource_pack$external_host$sha1;
|
||||
protected UUID resource_pack$external_host$uuid;
|
||||
|
||||
protected int performance$max_block_chain_update_limit;
|
||||
protected int performance$max_emojis_per_parse;
|
||||
|
||||
@@ -215,23 +204,12 @@ public class Config {
|
||||
resource_pack$supported_version$min = getVersion(config.get("resource-pack.supported-version.min", "1.20").toString());
|
||||
resource_pack$supported_version$max = getVersion(config.get("resource-pack.supported-version.max", "LATEST").toString());
|
||||
resource_pack$merge_external_folders = config.getStringList("resource-pack.merge-external-folders");
|
||||
resource_pack$send$mode = HostMode.valueOf(config.getString("resource-pack.send.mode", "self-host").replace("-", "_").toUpperCase(Locale.ENGLISH));
|
||||
resource_pack$send$self_host$port = config.getInt("resource-pack.send.self-host.port", 8163);
|
||||
resource_pack$send$self_host$ip = config.getString("resource-pack.send.self-host.ip", "localhost");
|
||||
resource_pack$self_host$local_file_path = config.getString("resource-pack.send.self-host.local-file-path", "./generated/resource_pack.zip");
|
||||
resource_pack$send$self_host$protocol = config.getString("resource-pack.send.self-host.protocol", "http");
|
||||
resource_pack$send$send_on_join = config.getBoolean("resource-pack.send.send-on-join", true);
|
||||
resource_pack$send$send_on_reload = config.getBoolean("resource-pack.send.send-on-reload", true);
|
||||
resource_pack$send$kick_if_declined = config.getBoolean("resource-pack.send.kick-if-declined", true);
|
||||
resource_pack$external_host$url = config.getString("resource-pack.send.external-host.url", "");
|
||||
resource_pack$external_host$sha1 = config.getString("resource-pack.send.external-host.sha1", "");
|
||||
String packUUIDStr = config.getString("resource-pack.send.external-host.uuid", "");
|
||||
resource_pack$external_host$uuid = packUUIDStr.isEmpty() ? UUID.nameUUIDFromBytes(resource_pack$external_host$url.getBytes(StandardCharsets.UTF_8)) : UUID.fromString(packUUIDStr);
|
||||
resource_pack$send$prompt = AdventureHelper.miniMessage().deserialize(config.getString("resource-pack.send.prompt", "<yellow>To fully experience our server, please accept our custom resource pack.</yellow>"));
|
||||
resource_pack$send$self_host$rate_limit$reset_interval = config.getLong("resource-pack.send.self-host.rate-limit.reset-interval", 30L);
|
||||
resource_pack$send$self_host$rate_limit$max_requests = config.getInt("resource-pack.send.self-host.rate-limit.max-requests", 3);
|
||||
resource_pack$send$self_host$deny_non_minecraft_request = config.getBoolean("resource-pack.send.deny-non-minecraft-request", true);
|
||||
|
||||
resource_pack$delivery$send_on_join = config.getBoolean("resource-pack.delivery.send-on-join", true);
|
||||
resource_pack$delivery$resend_on_upload = config.getBoolean("resource-pack.delivery.resend-on-upload", true);
|
||||
resource_pack$delivery$kick_if_declined = config.getBoolean("resource-pack.delivery.kick-if-declined", true);
|
||||
resource_pack$delivery$auto_upload = config.getBoolean("resource-pack.delivery.auto-upload", true);
|
||||
resource_pack$delivery$file_to_upload = resolvePath(config.getString("resource-pack.delivery.file-to-upload", "./generated/resource_pack.zip"));
|
||||
resource_pack$send$prompt = AdventureHelper.miniMessage().deserialize(config.getString("resource-pack.delivery.prompt", "<yellow>To fully experience our server, please accept our custom resource pack.</yellow>"));
|
||||
resource_pack$protection$crash_tools$method_1 = config.getBoolean("resource-pack.protection.crash-tools.method-1", false);
|
||||
resource_pack$protection$crash_tools$method_2 = config.getBoolean("resource-pack.protection.crash-tools.method-2", false);
|
||||
resource_pack$protection$crash_tools$method_3 = config.getBoolean("resource-pack.protection.crash-tools.method-3", false);
|
||||
@@ -240,7 +218,6 @@ public class Config {
|
||||
resource_pack$protection$obfuscation$fake_directory = config.getBoolean("resource-pack.protection.obfuscation.fake-directory", false);
|
||||
resource_pack$protection$obfuscation$escape_unicode = config.getBoolean("resource-pack.protection.obfuscation.escape-unicode", false);
|
||||
resource_pack$protection$obfuscation$break_json = config.getBoolean("resource-pack.protection.obfuscation.break-json", false);
|
||||
|
||||
resource_pack$protection$obfuscation$resource_location$enable = config.getBoolean("resource-pack.protection.obfuscation.resource-location.enable", false);
|
||||
resource_pack$protection$obfuscation$resource_location$random_namespace$amount = config.getInt("resource-pack.protection.obfuscation.resource-location.random-namespace.amount", 32);
|
||||
resource_pack$protection$obfuscation$resource_location$random_namespace$length = config.getInt("resource-pack.protection.obfuscation.resource-location.random-namespace.length", 8);
|
||||
@@ -443,10 +420,6 @@ public class Config {
|
||||
return instance.chunk_system$restore_vanilla_blocks_on_chunk_unload && instance.chunk_system$restore_custom_blocks_on_chunk_load;
|
||||
}
|
||||
|
||||
public static boolean denyNonMinecraftRequest() {
|
||||
return instance.resource_pack$send$self_host$deny_non_minecraft_request;
|
||||
}
|
||||
|
||||
public static boolean restoreCustomBlocks() {
|
||||
return instance.chunk_system$restore_custom_blocks_on_chunk_load;
|
||||
}
|
||||
@@ -459,60 +432,28 @@ public class Config {
|
||||
return instance.resource_pack$merge_external_folders;
|
||||
}
|
||||
|
||||
public static HostMode hostMode() {
|
||||
return instance.resource_pack$send$mode;
|
||||
}
|
||||
|
||||
public static String hostIP() {
|
||||
return instance.resource_pack$send$self_host$ip;
|
||||
}
|
||||
|
||||
public static int hostPort() {
|
||||
return instance.resource_pack$send$self_host$port;
|
||||
}
|
||||
|
||||
public static boolean kickOnDeclined() {
|
||||
return instance.resource_pack$send$kick_if_declined;
|
||||
return instance.resource_pack$delivery$kick_if_declined;
|
||||
}
|
||||
|
||||
public static Component resourcePackPrompt() {
|
||||
return instance.resource_pack$send$prompt;
|
||||
}
|
||||
|
||||
public static String hostProtocol() {
|
||||
return instance.resource_pack$send$self_host$protocol;
|
||||
}
|
||||
|
||||
public static String externalPackUrl() {
|
||||
return instance.resource_pack$external_host$url;
|
||||
}
|
||||
|
||||
public static String externalPackSha1() {
|
||||
return instance.resource_pack$external_host$sha1;
|
||||
}
|
||||
|
||||
public static UUID externalPackUUID() {
|
||||
return instance.resource_pack$external_host$uuid;
|
||||
}
|
||||
|
||||
public static boolean sendPackOnJoin() {
|
||||
return instance.resource_pack$send$send_on_join;
|
||||
return instance.resource_pack$delivery$send_on_join;
|
||||
}
|
||||
|
||||
public static boolean sendPackOnReload() {
|
||||
return instance.resource_pack$send$send_on_reload;
|
||||
public static boolean sendPackOnUpload() {
|
||||
return instance.resource_pack$delivery$resend_on_upload;
|
||||
}
|
||||
|
||||
public static int requestRate() {
|
||||
return instance.resource_pack$send$self_host$rate_limit$max_requests;
|
||||
public static boolean autoUpload() {
|
||||
return instance.resource_pack$delivery$auto_upload;
|
||||
}
|
||||
|
||||
public static long requestInterval() {
|
||||
return instance.resource_pack$send$self_host$rate_limit$reset_interval;
|
||||
}
|
||||
|
||||
public static String hostResourcePackPath() {
|
||||
return instance.resource_pack$self_host$local_file_path;
|
||||
public static Path fileToUpload() {
|
||||
return instance.resource_pack$delivery$file_to_upload;
|
||||
}
|
||||
|
||||
public static List<ConditionalResolution> resolutions() {
|
||||
@@ -757,6 +698,10 @@ public class Config {
|
||||
return configFile;
|
||||
}
|
||||
|
||||
private Path resolvePath(String path) {
|
||||
return path.startsWith(".") ? CraftEngine.instance().dataFolderPath().resolve(path) : Path.of(path);
|
||||
}
|
||||
|
||||
public YamlDocument settings() {
|
||||
if (config == null) {
|
||||
throw new IllegalStateException("Main config not loaded");
|
||||
|
||||
@@ -14,6 +14,7 @@ public class Dependencies {
|
||||
"asm",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency ASM_COMMONS = new Dependency(
|
||||
"asm-commons",
|
||||
"org.ow2.asm",
|
||||
@@ -21,6 +22,7 @@ public class Dependencies {
|
||||
"asm-commons",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency JAR_RELOCATOR = new Dependency(
|
||||
"jar-relocator",
|
||||
"me.lucko",
|
||||
@@ -28,13 +30,7 @@ public class Dependencies {
|
||||
"jar-relocator",
|
||||
Collections.emptyList()
|
||||
);
|
||||
public static final Dependency NETTY_HTTP = new Dependency(
|
||||
"netty-codec-http",
|
||||
"io.netty",
|
||||
"netty-codec-http",
|
||||
"netty-codec-http",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency GEANTY_REF = new Dependency(
|
||||
"geantyref",
|
||||
"io{}leangen{}geantyref",
|
||||
@@ -42,6 +38,7 @@ public class Dependencies {
|
||||
"geantyref",
|
||||
List.of(Relocation.of("geantyref", "io{}leangen{}geantyref"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_CORE = new Dependency(
|
||||
"cloud-core",
|
||||
"org{}incendo",
|
||||
@@ -50,6 +47,7 @@ public class Dependencies {
|
||||
List.of(Relocation.of("cloud", "org{}incendo{}cloud"),
|
||||
Relocation.of("geantyref", "io{}leangen{}geantyref"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_BRIGADIER = new Dependency(
|
||||
"cloud-brigadier",
|
||||
"org{}incendo",
|
||||
@@ -58,6 +56,7 @@ public class Dependencies {
|
||||
List.of(Relocation.of("cloud", "org{}incendo{}cloud"),
|
||||
Relocation.of("geantyref", "io{}leangen{}geantyref"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_SERVICES = new Dependency(
|
||||
"cloud-services",
|
||||
"org{}incendo",
|
||||
@@ -66,6 +65,7 @@ public class Dependencies {
|
||||
List.of(Relocation.of("cloud", "org{}incendo{}cloud"),
|
||||
Relocation.of("geantyref", "io{}leangen{}geantyref"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_BUKKIT = new Dependency(
|
||||
"cloud-bukkit",
|
||||
"org{}incendo",
|
||||
@@ -77,6 +77,7 @@ public class Dependencies {
|
||||
Relocation.of("examination", "net{}kyori{}examination"),
|
||||
Relocation.of("option", "net{}kyori{}option"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_PAPER = new Dependency(
|
||||
"cloud-paper",
|
||||
"org{}incendo",
|
||||
@@ -88,6 +89,7 @@ public class Dependencies {
|
||||
Relocation.of("examination", "net{}kyori{}examination"),
|
||||
Relocation.of("option", "net{}kyori{}option"))
|
||||
);
|
||||
|
||||
public static final Dependency CLOUD_MINECRAFT_EXTRAS = new Dependency(
|
||||
"cloud-minecraft-extras",
|
||||
"org{}incendo",
|
||||
@@ -99,6 +101,7 @@ public class Dependencies {
|
||||
Relocation.of("examination", "net{}kyori{}examination"),
|
||||
Relocation.of("option", "net{}kyori{}option"))
|
||||
);
|
||||
|
||||
public static final Dependency BOOSTED_YAML = new Dependency(
|
||||
"boosted-yaml",
|
||||
"dev{}dejvokep",
|
||||
@@ -106,6 +109,7 @@ public class Dependencies {
|
||||
"boosted-yaml",
|
||||
List.of(Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml"))
|
||||
);
|
||||
|
||||
public static final Dependency BSTATS_BASE = new Dependency(
|
||||
"bstats-base",
|
||||
"org{}bstats",
|
||||
@@ -113,6 +117,7 @@ public class Dependencies {
|
||||
"bstats-base",
|
||||
List.of(Relocation.of("bstats", "org{}bstats"))
|
||||
);
|
||||
|
||||
public static final Dependency BSTATS_BUKKIT = new Dependency(
|
||||
"bstats-bukkit",
|
||||
"org{}bstats",
|
||||
@@ -125,6 +130,7 @@ public class Dependencies {
|
||||
return Dependencies.BSTATS_BASE.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency GSON = new Dependency(
|
||||
"gson",
|
||||
"com.google.code.gson",
|
||||
@@ -132,6 +138,7 @@ public class Dependencies {
|
||||
"gson",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency CAFFEINE = new Dependency(
|
||||
"caffeine",
|
||||
"com{}github{}ben-manes{}caffeine",
|
||||
@@ -139,6 +146,7 @@ public class Dependencies {
|
||||
"caffeine",
|
||||
List.of(Relocation.of("caffeine", "com{}github{}benmanes{}caffeine"))
|
||||
);
|
||||
|
||||
public static final Dependency ZSTD = new Dependency(
|
||||
"zstd-jni",
|
||||
"com.github.luben",
|
||||
@@ -146,6 +154,7 @@ public class Dependencies {
|
||||
"zstd-jni",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency SLF4J_API = new Dependency(
|
||||
"slf4j-api",
|
||||
"org.slf4j",
|
||||
@@ -153,6 +162,7 @@ public class Dependencies {
|
||||
"slf4j-api",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency SLF4J_SIMPLE = new Dependency(
|
||||
"slf4j-simple",
|
||||
"org.slf4j",
|
||||
@@ -165,6 +175,7 @@ public class Dependencies {
|
||||
return Dependencies.SLF4J_API.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency COMMONS_IO = new Dependency(
|
||||
"commons-io",
|
||||
"commons-io",
|
||||
@@ -172,6 +183,7 @@ public class Dependencies {
|
||||
"commons-io",
|
||||
List.of(Relocation.of("commons", "org{}apache{}commons"))
|
||||
);
|
||||
|
||||
public static final Dependency BYTE_BUDDY = new Dependency(
|
||||
"byte-buddy",
|
||||
"net{}bytebuddy",
|
||||
@@ -179,6 +191,7 @@ public class Dependencies {
|
||||
"byte-buddy",
|
||||
List.of(Relocation.of("bytebuddy", "net{}bytebuddy"))
|
||||
);
|
||||
|
||||
public static final Dependency SNAKE_YAML = new Dependency(
|
||||
"snake-yaml",
|
||||
"org{}yaml",
|
||||
@@ -186,6 +199,7 @@ public class Dependencies {
|
||||
"snakeyaml",
|
||||
List.of(Relocation.of("snakeyaml", "org{}yaml{}snakeyaml"))
|
||||
);
|
||||
|
||||
public static final Dependency MINIMESSAGE = new Dependency(
|
||||
"adventure-text-minimessage",
|
||||
"net{}kyori",
|
||||
@@ -193,6 +207,7 @@ public class Dependencies {
|
||||
"adventure-text-minimessage",
|
||||
List.of(Relocation.of("adventure", "net{}kyori{}adventure"))
|
||||
);
|
||||
|
||||
public static final Dependency TEXT_SERIALIZER_GSON = new Dependency(
|
||||
"adventure-text-serializer-gson",
|
||||
"net{}kyori",
|
||||
@@ -200,6 +215,7 @@ public class Dependencies {
|
||||
"adventure-text-serializer-gson",
|
||||
List.of(Relocation.of("adventure", "net{}kyori{}adventure"))
|
||||
);
|
||||
|
||||
public static final Dependency TEXT_SERIALIZER_GSON_LEGACY = new Dependency(
|
||||
"adventure-text-serializer-json-legacy-impl",
|
||||
"net{}kyori",
|
||||
@@ -207,6 +223,7 @@ public class Dependencies {
|
||||
"adventure-text-serializer-json-legacy-impl",
|
||||
List.of(Relocation.of("adventure", "net{}kyori{}adventure"))
|
||||
);
|
||||
|
||||
public static final Dependency TEXT_SERIALIZER_JSON = new Dependency(
|
||||
"adventure-text-serializer-json",
|
||||
"net{}kyori",
|
||||
@@ -214,6 +231,7 @@ public class Dependencies {
|
||||
"adventure-text-serializer-json",
|
||||
List.of(Relocation.of("adventure", "net{}kyori{}adventure"))
|
||||
);
|
||||
|
||||
public static final Dependency AHO_CORASICK = new Dependency(
|
||||
"ahocorasick",
|
||||
"org{}ahocorasick",
|
||||
@@ -221,6 +239,7 @@ public class Dependencies {
|
||||
"aho-corasick",
|
||||
List.of(Relocation.of("ahocorasick", "org{}ahocorasick"))
|
||||
);
|
||||
|
||||
public static final Dependency LZ4 = new Dependency(
|
||||
"lz4",
|
||||
"org{}lz4",
|
||||
@@ -228,4 +247,482 @@ public class Dependencies {
|
||||
"lz4-java",
|
||||
List.of(Relocation.of("jpountz", "net{}jpountz"))
|
||||
);
|
||||
|
||||
public static final Dependency NETTY_HTTP = new Dependency(
|
||||
"netty-codec-http",
|
||||
"io{}netty",
|
||||
"netty-codec-http",
|
||||
"netty-codec-http",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency NETTY_HTTP2 = new Dependency(
|
||||
"netty-codec-http2",
|
||||
"io{}netty",
|
||||
"netty-codec-http2",
|
||||
"netty-codec-http2",
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
public static final Dependency REACTIVE_STREAMS = new Dependency(
|
||||
"reactive-streams",
|
||||
"org{}reactivestreams",
|
||||
"reactive-streams",
|
||||
"reactive-streams",
|
||||
List.of(Relocation.of("reactivestreams", "org{}reactivestreams"))
|
||||
);
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_S3 = new Dependency(
|
||||
"amazon-sdk-s3",
|
||||
"software{}amazon{}awssdk",
|
||||
"s3",
|
||||
"amazon-s3",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_NETTY_NIO_CLIENT = new Dependency(
|
||||
"amazon-sdk-netty-nio-client",
|
||||
"software{}amazon{}awssdk",
|
||||
"netty-nio-client",
|
||||
"amazon-netty-nio-client",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_SDK_CORE = new Dependency(
|
||||
"amazon-sdk-core",
|
||||
"software{}amazon{}awssdk",
|
||||
"sdk-core",
|
||||
"amazon-sdk-core",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_AUTH = new Dependency(
|
||||
"amazon-sdk-auth",
|
||||
"software{}amazon{}awssdk",
|
||||
"auth",
|
||||
"amazon-auth",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_REGIONS = new Dependency(
|
||||
"amazon-sdk-regions",
|
||||
"software{}amazon{}awssdk",
|
||||
"regions",
|
||||
"amazon-regions",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_IDENTITY_SPI = new Dependency(
|
||||
"amazon-sdk-identity-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"identity-spi",
|
||||
"amazon-identity-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_HTTP_CLIENT_SPI = new Dependency(
|
||||
"amazon-sdk-http-client-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"http-client-spi",
|
||||
"amazon-http-client-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_PROTOCOL_CORE = new Dependency(
|
||||
"amazon-sdk-protocol-core",
|
||||
"software{}amazon{}awssdk",
|
||||
"protocol-core",
|
||||
"amazon-protocol-core",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_AWS_XML_PROTOCOL = new Dependency(
|
||||
"amazon-sdk-aws-xml-protocol",
|
||||
"software{}amazon{}awssdk",
|
||||
"aws-xml-protocol",
|
||||
"amazon-aws-xml-protocol",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_JSON_UTILS = new Dependency(
|
||||
"amazon-sdk-json-utils",
|
||||
"software{}amazon{}awssdk",
|
||||
"json-utils",
|
||||
"amazon-json-utils",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_AWS_CORE = new Dependency(
|
||||
"amazon-sdk-aws-core",
|
||||
"software{}amazon{}awssdk",
|
||||
"aws-core",
|
||||
"amazon-aws-core",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_UTILS = new Dependency(
|
||||
"amazon-sdk-utils",
|
||||
"software{}amazon{}awssdk",
|
||||
"utils",
|
||||
"amazon-utils",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_ANNOTATIONS = new Dependency(
|
||||
"amazon-sdk-annotations",
|
||||
"software{}amazon{}awssdk",
|
||||
"annotations",
|
||||
"amazon-annotations",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_CRT_CORE = new Dependency(
|
||||
"amazon-sdk-crt-core",
|
||||
"software{}amazon{}awssdk",
|
||||
"crt-core",
|
||||
"amazon-crt-core",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_CHECKSUMS = new Dependency(
|
||||
"amazon-sdk-checksums",
|
||||
"software{}amazon{}awssdk",
|
||||
"checksums",
|
||||
"amazon-checksums",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_EVENTSTREAM = new Dependency(
|
||||
"amazon-sdk-eventstream",
|
||||
"software{}amazon{}eventstream",
|
||||
"eventstream",
|
||||
"amazon-eventstream",
|
||||
List.of(
|
||||
Relocation.of("eventstream", "software{}amazon{}eventstream"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_PROFILES = new Dependency(
|
||||
"amazon-sdk-profiles",
|
||||
"software{}amazon{}awssdk",
|
||||
"profiles",
|
||||
"amazon-profiles",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_RETRIES = new Dependency(
|
||||
"amazon-sdk-retries",
|
||||
"software{}amazon{}awssdk",
|
||||
"retries",
|
||||
"amazon-retries",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_ENDPOINTS_SPI = new Dependency(
|
||||
"amazon-sdk-endpoints-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"endpoints-spi",
|
||||
"amazon-endpoints-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_ARNS = new Dependency(
|
||||
"amazon-sdk-arns",
|
||||
"software{}amazon{}awssdk",
|
||||
"arns",
|
||||
"amazon-arns",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_AWS_QUERY_PROTOCOL = new Dependency(
|
||||
"amazon-sdk-aws-query-protocol",
|
||||
"software{}amazon{}awssdk",
|
||||
"aws-query-protocol",
|
||||
"amazon-aws-query-protocol",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_HTTP_AUTH_AWS = new Dependency(
|
||||
"amazon-sdk-http-auth-aws",
|
||||
"software{}amazon{}awssdk",
|
||||
"http-auth-aws",
|
||||
"amazon-http-auth-aws",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_HTTP_AUTH_SPI = new Dependency(
|
||||
"amazon-sdk-http-auth-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"http-auth-spi",
|
||||
"amazon-http-auth-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_HTTP_AUTH = new Dependency(
|
||||
"amazon-sdk-http-auth",
|
||||
"software{}amazon{}awssdk",
|
||||
"http-auth",
|
||||
"amazon-http-auth",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_HTTP_AUTH_AWS_EVENTSTREAM = new Dependency(
|
||||
"amazon-sdk-http-auth-aws-eventstream",
|
||||
"software{}amazon{}awssdk",
|
||||
"http-auth-aws-eventstream",
|
||||
"amazon-http-auth-aws-eventstream",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_CHECKSUMS_SPI = new Dependency(
|
||||
"amazon-sdk-checksums-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"checksums-spi",
|
||||
"amazon-checksums-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_RETRIES_SPI = new Dependency(
|
||||
"amazon-sdk-retries-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"retries-spi",
|
||||
"amazon-retries-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_METRICS_SPI = new Dependency(
|
||||
"amazon-sdk-metrics-spi",
|
||||
"software{}amazon{}awssdk",
|
||||
"metrics-spi",
|
||||
"amazon-metrics-spi",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Dependency AMAZON_AWSSDK_THIRD_PARTY_JACKSON_CORE = new Dependency(
|
||||
"amazon-sdk-third-party-jackson-core",
|
||||
"software{}amazon{}awssdk",
|
||||
"third-party-jackson-core",
|
||||
"amazon-third-party-jackson-core",
|
||||
List.of(
|
||||
Relocation.of("awssdk", "software{}amazon{}awssdk"),
|
||||
Relocation.of("reactivestreams", "org{}reactivestreams")
|
||||
)
|
||||
) {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return AMAZON_AWSSDK_S3.getVersion();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.plugin.gui.Ingredient;
|
||||
import net.momirealms.craftengine.core.plugin.gui.*;
|
||||
import net.momirealms.craftengine.core.plugin.gui.Ingredient;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
@@ -26,4 +26,8 @@ public interface MessageConstants {
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_SUCCESS = Component.translatable().key("command.resource.disable.success");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_FAILURE = Component.translatable().key("command.resource.disable.failure.unknown");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_LIST = Component.translatable().key("command.resource.list");
|
||||
TranslatableComponent.Builder COMMAND_UPLOAD_FAILURE_NOT_SUPPORTED = Component.translatable().key("command.upload.failure.not_supported");
|
||||
TranslatableComponent.Builder COMMAND_UPLOAD_ON_PROGRESS = Component.translatable().key("command.upload.on_progress");
|
||||
TranslatableComponent.Builder COMMAND_SEND_RESOURCE_PACK_SUCCESS_SINGLE = Component.translatable().key("command.send_resource_pack.success.single");
|
||||
TranslatableComponent.Builder COMMAND_SEND_RESOURCE_PACK_SUCCESS_MULTIPLE = Component.translatable().key("command.send_resource_pack.success.multiple");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface NetWorkUser {
|
||||
boolean isOnline();
|
||||
@@ -17,9 +18,15 @@ public interface NetWorkUser {
|
||||
|
||||
String name();
|
||||
|
||||
void setName(String name);
|
||||
|
||||
UUID uuid();
|
||||
|
||||
void setUUID(UUID uuid);
|
||||
|
||||
void sendPacket(Object packet, boolean immediately);
|
||||
|
||||
void receivePacket(Object packet);
|
||||
void simulatePacket(Object packet);
|
||||
|
||||
@ApiStatus.Internal
|
||||
ConnectionState decoderState();
|
||||
@@ -42,4 +49,6 @@ public interface NetWorkUser {
|
||||
boolean clientModEnabled();
|
||||
|
||||
void setClientModState(boolean enable);
|
||||
|
||||
void addResourcePackUUID(UUID uuid);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunctionFactory;
|
||||
import net.momirealms.craftengine.core.loot.number.NumberProviderFactory;
|
||||
import net.momirealms.craftengine.core.pack.conflict.matcher.PathMatcherFactory;
|
||||
import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModelFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyFactory;
|
||||
@@ -47,6 +48,7 @@ public class BuiltInRegistries {
|
||||
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createRegistry(Registries.RESOLUTION_FACTORY);
|
||||
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory> SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY);
|
||||
public static final Registry<HitBoxFactory> HITBOX_FACTORY = createRegistry(Registries.HITBOX_FACTORY);
|
||||
public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createRegistry(Registries.RESOURCE_PACK_HOST_FACTORY);
|
||||
|
||||
private static <T> Registry<T> createRegistry(ResourceKey<? extends Registry<T>> key) {
|
||||
return new MappedRegistry<>(key);
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunctionFactory;
|
||||
import net.momirealms.craftengine.core.loot.number.NumberProviderFactory;
|
||||
import net.momirealms.craftengine.core.pack.conflict.matcher.PathMatcherFactory;
|
||||
import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionFactory;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.ItemModelFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyFactory;
|
||||
import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyFactory;
|
||||
@@ -48,4 +49,5 @@ public class Registries {
|
||||
public static final ResourceKey<Registry<ResolutionFactory>> RESOLUTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory"));
|
||||
public static final ResourceKey<Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory>> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory"));
|
||||
public static final ResourceKey<Registry<HitBoxFactory>> HITBOX_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory"));
|
||||
public static final ResourceKey<Registry<ResourcePackHostFactory>> RESOURCE_PACK_HOST_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.momirealms.craftengine.core.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CompletableFutures {
|
||||
|
||||
private CompletableFutures() {}
|
||||
|
||||
public static <T extends CompletableFuture<?>> Collector<T, ImmutableList.Builder<T>, CompletableFuture<Void>> collector() {
|
||||
return Collector.of(
|
||||
ImmutableList.Builder::new,
|
||||
ImmutableList.Builder::add,
|
||||
(l, r) -> l.addAll(r.build()),
|
||||
builder -> allOf(builder.build())
|
||||
);
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> allOf(Stream<? extends CompletableFuture<?>> futures) {
|
||||
CompletableFuture<?>[] arr = futures.toArray(CompletableFuture[]::new);
|
||||
return CompletableFuture.allOf(arr);
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> allOf(Collection<? extends CompletableFuture<?>> futures) {
|
||||
CompletableFuture<?>[] arr = futures.toArray(new CompletableFuture[0]);
|
||||
return CompletableFuture.allOf(arr);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,34 @@ public enum Direction {
|
||||
this.adjZ = vector.z();
|
||||
}
|
||||
|
||||
public static Direction fromYaw(float yaw) {
|
||||
yaw = normalizeAngle(yaw);
|
||||
if (yaw < 45) {
|
||||
if (yaw > -45) {
|
||||
return NORTH;
|
||||
} else if (yaw > -135) {
|
||||
return EAST;
|
||||
} else {
|
||||
return SOUTH;
|
||||
}
|
||||
} else {
|
||||
if (yaw < 135) {
|
||||
return WEST;
|
||||
} else {
|
||||
return SOUTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float normalizeAngle(float angle) {
|
||||
angle %= 360;
|
||||
angle = (angle + 360) % 360;
|
||||
if (angle > 180) {
|
||||
angle -= 360;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
public HorizontalDirection toHorizontalDirection() {
|
||||
return switch (this) {
|
||||
case DOWN, UP -> null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.core.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
@@ -78,4 +79,26 @@ public class GsonHelper {
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
public static JsonObject parseJsonToJsonObject(String json) {
|
||||
try {
|
||||
return get().fromJson(
|
||||
json,
|
||||
JsonObject.class
|
||||
);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new RuntimeException("Invalid JSON response: " + json, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> parseJsonToMap(String json) {
|
||||
try {
|
||||
return GsonHelper.get().fromJson(
|
||||
json,
|
||||
new TypeToken<Map<String, Object>>() {}.getType()
|
||||
);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new RuntimeException("Invalid JSON response: " + json, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.momirealms.craftengine.core.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
public class HashUtils {
|
||||
public static String calculateLocalFileSha1(Path filePath) {
|
||||
try (InputStream is = Files.newInputStream(filePath)) {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, len);
|
||||
}
|
||||
byte[] digest = md.digest();
|
||||
return HexFormat.of().formatHex(digest);
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Failed to calculate SHA1", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import org.joml.Quaternionf;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProxySelector;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -172,4 +174,19 @@ public class MiscUtils {
|
||||
throw new RuntimeException("Cannot convert " + o + " to Quaternionf");
|
||||
}
|
||||
}
|
||||
|
||||
public static ProxySelector getProxySelector(Object o) {
|
||||
Map<String, Object> proxySetting = castToMap(o, true);
|
||||
ProxySelector proxy = ProxySelector.getDefault();
|
||||
if (proxySetting != null) {
|
||||
String proxyHost = (String) proxySetting.get("host");
|
||||
int proxyPort = (int) proxySetting.get("port");
|
||||
if (proxyHost == null || proxyHost.isEmpty() || proxyPort <= 0 || proxyPort > 65535) {
|
||||
throw new IllegalArgumentException("Invalid proxy setting");
|
||||
} else {
|
||||
proxy = ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort));
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Project settings
|
||||
# Rule: [major update].[feature update].[bug fix]
|
||||
project_version=0.0.48.1
|
||||
config_version=27
|
||||
lang_version=4
|
||||
project_version=0.0.49-beta.4
|
||||
config_version=29
|
||||
lang_version=6
|
||||
project_group=net.momirealms
|
||||
latest_supported_version=1.21.5
|
||||
latest_minecraft_version=1.21.5
|
||||
@@ -41,7 +41,7 @@ geantyref_version=1.3.16
|
||||
zstd_version=1.5.7-2
|
||||
commons_io_version=2.18.0
|
||||
sparrow_nbt_version=0.6.2
|
||||
sparrow_util_version=0.38
|
||||
sparrow_util_version=0.39
|
||||
fastutil_version=8.5.15
|
||||
netty_version=4.1.119.Final
|
||||
joml_version=1.10.8
|
||||
@@ -50,8 +50,11 @@ mojang_brigadier_version=1.0.18
|
||||
byte_buddy_version=1.17.5
|
||||
ahocorasick_version=0.6.3
|
||||
snake_yaml_version=2.4
|
||||
anti_grief_version=0.13
|
||||
nms_helper_version=0.59
|
||||
anti_grief_version=0.15
|
||||
nms_helper_version=0.59.7
|
||||
reactive_streams_version=1.0.4
|
||||
amazon_awssdk_version=2.31.23
|
||||
amazon_awssdk_eventstream_version=1.0.1
|
||||
# Ignite Dependencies
|
||||
mixinextras_version=0.4.1
|
||||
mixin_version=0.15.2+mixin.0.8.7
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package net.momirealms.craftengine.mod;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
@@ -11,7 +8,6 @@ import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.CodeSource;
|
||||
|
||||
Reference in New Issue
Block a user