9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

Merge branch 'Xiao-MoMi:dev' into dev

This commit is contained in:
jhqwqmc
2025-10-24 00:24:13 +08:00
committed by GitHub
28 changed files with 643 additions and 132 deletions

View File

@@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.util.Tristate;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Registry;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -416,19 +417,26 @@ public final class BukkitBlockManager extends AbstractBlockManager {
@SuppressWarnings("unchecked")
private void deceiveBukkitRegistry() {
try {
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
Set<String> invalid = new HashSet<>();
for (int i = 0; i < this.customBlocks.length; i++) {
DelegatingBlock customBlock = this.customBlocks[i];
String value = Config.deceiveBukkitMaterial(i).value();
Material material;
try {
material = Material.valueOf(Config.deceiveBukkitMaterial().value().toUpperCase(Locale.ROOT));
material = Material.valueOf(value.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
this.plugin.logger().warn(Config.deceiveBukkitMaterial() + " is not a valid material", e);
material = Material.STONE;
if (invalid.add(value)) {
this.plugin.logger().warn("Cannot load 'deceive-bukkit-material'. '" + value + "' is an invalid bukkit material", e);
}
material = Material.BRICKS;
}
if (!material.isBlock()) {
this.plugin.logger().warn(Config.deceiveBukkitMaterial() + " is not a valid bukkit block material");
material = Material.STONE;
if (invalid.add(value)) {
this.plugin.logger().warn("Cannot load 'deceive-bukkit-material'. '" + value + "' is an invalid bukkit block material");
}
material = Material.BRICKS;
}
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
for (DelegatingBlock customBlock : this.customBlocks) {
magicMap.put(customBlock, material);
}
} catch (ReflectiveOperationException e) {

View File

@@ -13,57 +13,6 @@ resource-pack:
# This option determines the location of the generated resource pack
# You can use either an absolute path or a relative path here
path: "./generated/resource_pack.zip"
# Should those images in minecraft:default font also work in minecraft:uniform
override-uniform-font: true
# Generate assets for CraftEngine fabric mod
# Note: fabric mod is used for clientside Axiom/WorldEdit mod
generate-mod-assets: false
# Resource pack protection
protection:
# Warning: Do not attempt to unzip the resource pack with crash tools enabled.
# You can enable all the methods at the same time.
crash-tools:
method-1: false
method-2: false
method-3: false # Enable this would increase the resource pack size by 0.67MB
# [Premium Exclusive]
# Obfuscate your resource pack
obfuscation:
enable: false
seed: 0 # 0 = random seed
fake-directory: false
escape-unicode: false
resource-location:
enable: true
random-namespace:
amount: 16 # 0 = disable
length: 9
random-path:
source: obf
depth: 16
anti-unzip: false
random-atlas:
images-per-canvas: 32 # 0 = disable
# Sometimes, some vanilla files that have been overwritten might be mistakenly obfuscated.
# Please add the ignored textures/models/sounds here.
bypass-textures:
# - minecraft:block/farmland
- "@legacy_unicode"
- "@vanilla_textures"
bypass-models:
- "@vanilla_models"
bypass-sounds: []
bypass-equipments: []
# Validate if there is any error in the resource pack, such as missing textures or models
# If your resource pack is compliant with the standard, you can disable validation to improve the resource pack generation speed.
validation:
enable: true
# [Premium Exclusive]
# Fix images that are not within the texture atlas. It is unreasonable to always rely on plugins to fix your mistakes.
# You should strive to make your resource pack more standardized after gaining some experience with resource packs.
fix-atlas: true
# Define the name of the overlay folders
overlay-format: "ce_overlay_{version}"
# Allowed values:
# - 1.20.1, 1.21, 1.21.8, etc.
# - LATEST: the latest client version
@@ -73,42 +22,24 @@ resource-pack:
max: LATEST
# Remove 1.21.5+ tinted_leaves particles
remove-tinted-leaves-particle: true
# Define the name of the overlay folders
overlay-format: "ce_overlay_{version}"
# Should those images in minecraft:default font also work in minecraft:uniform
override-uniform-font: true
# Generate assets for CraftEngine fabric mod
# Note: fabric mod is used for clientside Axiom/WorldEdit mod
generate-mod-assets: false
# Exclude the shaders when generating the resource pack
exclude-core-shaders: false
# Merge other packs
merge-external-folders:
- "ModelEngine/resource pack"
merge-external-zip-files:
- "CustomNameplates/resourcepack.zip"
- "BetterModel/build.zip"
exclude-file-extensions: ["md", "psd", "bbmodel", "db", "ini", "DS_Store"]
# Exclude the shaders when generating the resource pack
exclude-core-shaders: false
delivery:
# Send the resource pack on joining the server
send-on-join: true
kick-if-declined: true
kick-if-failed-to-apply: false
prompt: "<yellow>To fully experience our server,<newline>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.
# Read this page for more host types: https://xiao-momi.github.io/craft-engine-wiki/getting_start/set_up_host
hosting:
- type: "self"
ip: "localhost"
port: 8163
protocol: "http"
deny-non-minecraft-request: true
one-time-token: true
rate-limit:
max-requests: 10
reset-interval: 30
# 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
# Whether a verified player UUID is required to get the resource pack
strict-player-uuid-validation: true
# Provide the solution for when a file conflict is encountered
# https://xiao-momi.github.io/craft-engine-wiki/reference/file_conflict
duplicated-files-handler:
- term:
type: any_of
@@ -150,6 +81,88 @@ resource-pack:
suffix: "minecraft/atlases"
resolution:
type: merge_atlas
# Validate if there is any error in the resource pack, such as missing textures or models
# If your resource pack is compliant with the standard, you can disable validation to improve the resource pack generation speed.
validation:
enable: true
# [Premium Exclusive]
# Fix images that are not within the texture atlas. It is unreasonable to always rely on plugins to fix your mistakes.
# You should strive to make your resource pack more standardized after gaining some experience with resource packs.
fix-atlas: true
# Optimize your resource pack by reducing its size
# optimization:
# enable: true
# png:
# color-quantization: true
# lossless-bit-depth: true
# remove-metadata: true
# model:
# minimize: true
# bypass-textures: []
# Protect your resource pack from being cracked by others
protection:
# Prevent thieves from extracting your resource pack. These options will crash their software.
crash-tools:
method-1: false
method-2: false
method-3: false # Enable this would increase the resource pack size by 0.67MB
# [Premium Exclusive]
# Obfuscate your resource pack to prevent thieves from restoring its original structure
obfuscation:
enable: false
seed: 0 # 0 = random seed
fake-directory: false # Create fake folders to deceive thieves
escape-unicode: false # Escape the JSON to make it human-unreadable
resource-location:
enable: true
random-namespace:
amount: 8 # 0 = disable
length: 9
random-path:
source: obf
depth: 8
anti-unzip: false
random-atlas:
images-per-canvas: 256 # 0 = disable
# Sometimes, some vanilla files that have been overwritten might be mistakenly obfuscated
# Please add the ignored textures/models/sounds here if that happens
bypass-textures:
# - minecraft:block/farmland
- "@legacy_unicode"
- "@vanilla_textures"
bypass-models:
- "@vanilla_models"
bypass-sounds: []
bypass-equipments: []
# This section controls how to send the resource pack to players
delivery:
# Send the resource pack on joining the server
send-on-join: true
kick-if-declined: true
kick-if-failed-to-apply: false
prompt: "<yellow>To fully experience our server,<newline>please accept our custom resource pack.</yellow>"
# Whether a verified player UUID is required to download the resource pack
strict-player-uuid-validation: true
# Upload the resource pack automatically on generation
# When disabled, you must manually trigger uploads using the /ce upload command
auto-upload: true
# Resend the resource pack to players upon successful upload
resend-on-upload: true
# The file to upload
file-to-upload: "./generated/resource_pack.zip"
# 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.
# Read this page for more host types: https://xiao-momi.github.io/craft-engine-wiki/getting_start/set_up_host
hosting:
- type: "self"
ip: "localhost"
port: 8163
protocol: "http"
deny-non-minecraft-request: true
one-time-token: true
rate-limit:
max-requests: 10
reset-interval: 30
item:
# [Premium Exclusive]
@@ -208,7 +221,7 @@ equipment:
block:
# This decides the amount of real blocks on serverside. You should only consider increasing this value when your server state is insufficient.
# It is recommended to increase it by 500 each time. This option requires a restart to apply.
# It is recommended to increase it by 1000 each time. This option requires a restart to apply.
serverside-blocks: 2000
# Enables the sound system, which prevents the client from hearing some non-custom block sounds and improves the client experience.
sound-system:
@@ -238,7 +251,14 @@ block:
extended-interaction-range: 0.5
# Defines the value returned by Bukkit block.getMaterial()
# If another plugin causes incompatibility due to its reliance on this method, try changing this option to a different vanilla block.
deceive-bukkit-material: stone
deceive-bukkit-material:
default: bricks
# The numbers here represent the internal real IDs of the blocks.
# This means that overriding certain blocks needs to be done under the condition of forcibly assigning internal IDs.
# A restart is required to apply the changes.
overrides:
0: bricks
1~8: bricks
furniture:
# Hide technical entities used for storing furniture metadata.

View File

@@ -64,11 +64,8 @@ blocks:
- type: match_block_property
properties:
age: 2
- type: '!is_null'
argument: item_in_hand
- type: equals
value1: <arg:item_in_hand.id>
value2: default:ender_pearl_flower_seeds
- type: match_item
id: default:ender_pearl_flower_seeds
functions:
- type: break_block
x: <arg:block.block_x>

View File

@@ -132,10 +132,9 @@ blocks:
- type: match_block_property
properties:
age: 3
- type: 'is_null'
argument: item_in_hand
- type: hand
hand: main_hand
- type: '!has_item'
functions:
- type: break_block
x: <arg:block.block_x>

View File

@@ -472,6 +472,10 @@ warning.config.function.toast.invalid_advancement_type: "<yellow>Issue found in
warning.config.function.merchant_trade.missing_offers: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'offers' argument for 'merchant_trade' function.</yellow>"
warning.config.function.merchant_trade.offer.missing_cost_1: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'cost-1' argument for merchant trade offers.</yellow>"
warning.config.function.merchant_trade.offer.missing_result: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'result' argument for merchant trade offers.</yellow>"
warning.config.function.when.missing_source: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'source' argument for 'when' function.</yellow>"
warning.config.function.if_else.missing_rules: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'rules' argument for 'if_else' function.</yellow>"
warning.config.function.update_block_property.missing_properties: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'properties' argument for 'update_block_property' function.</yellow>"
warning.config.function.transform_block.missing_block: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'block' argument for 'transform_block' function.</yellow>"
warning.config.selector.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for selector.</yellow>"
warning.config.selector.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector type '<arg:2>'.</yellow>"
warning.config.selector.invalid_target: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector target '<arg:2>'.</yellow>"

View File

@@ -21,7 +21,7 @@ dependencies {
implementation("net.momirealms:sparrow-nbt-codec:${rootProject.properties["sparrow_nbt_version"]}")
implementation("net.momirealms:sparrow-nbt-legacy-codec:${rootProject.properties["sparrow_nbt_version"]}")
// S3
implementation("net.momirealms:craft-engine-s3:0.7")
implementation("net.momirealms:craft-engine-s3:0.8")
// Util
compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
// Adventure

View File

@@ -25,6 +25,7 @@ public class LootConditions {
register(CommonConditions.ANY_OF, new AnyOfCondition.FactoryImpl<>(LootConditions::fromMap));
register(CommonConditions.ALL_OF, new AllOfCondition.FactoryImpl<>(LootConditions::fromMap));
register(CommonConditions.HAS_PLAYER, new HasPlayerCondition.FactoryImpl<>());
register(CommonConditions.HAS_ITEM, new HasItemCondition.FactoryImpl<>());
register(CommonConditions.ENCHANTMENT, new EnchantmentCondition.Factory<>());
register(CommonConditions.INVERTED, new InvertedCondition.FactoryImpl<>(LootConditions::fromMap));
register(CommonConditions.FALLING_BLOCK, new FallingBlockCondition.FactoryImpl<>());

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.loot.LootConditions;
import net.momirealms.craftengine.core.loot.LootContext;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.*;
@@ -40,7 +41,7 @@ public class AlternativesLootEntryContainer<T> extends AbstractCompositeLootEntr
@SuppressWarnings("unchecked")
@Override
public LootEntryContainer<A> create(Map<String, Object> arguments) {
List<LootEntryContainer<A>> containers = Optional.ofNullable(arguments.get("children"))
List<LootEntryContainer<A>> containers = Optional.ofNullable(ResourceConfigUtils.get(arguments, "children", "terms", "branches"))
.map(it -> (List<LootEntryContainer<A>>) new ArrayList<LootEntryContainer<A>>(LootEntryContainers.fromMapList((List<Map<String, Object>>) it)))
.orElse(Collections.emptyList());
List<Condition<LootContext>> conditions = Optional.ofNullable(arguments.get("conditions"))

View File

@@ -14,12 +14,14 @@ import java.util.Map;
public class LootEntryContainers {
public static final Key ALTERNATIVES = Key.from("craftengine:alternatives");
public static final Key IF_ELSE = Key.from("craftengine:if_else");
public static final Key ITEM = Key.from("craftengine:item");
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
public static final Key EXP = Key.from("craftengine:exp");
static {
register(ALTERNATIVES, AlternativesLootEntryContainer.FACTORY);
register(IF_ELSE, AlternativesLootEntryContainer.FACTORY);
register(ITEM, SingleItemLootEntryContainer.FACTORY);
register(EXP, ExpLootEntryContainer.FACTORY);
register(FURNITURE_ITEM, FurnitureItemLootEntryContainer.FACTORY);

View File

@@ -126,7 +126,8 @@ public class Config {
protected int block$predict_breaking_interval;
protected double block$extended_interaction_range;
protected boolean block$chunk_relighter;
protected Key block$deceive_bukkit_material;
protected Key block$deceive_bukkit_material$default;
protected Map<Integer, Key> block$deceive_bukkit_material$overrides;
protected int block$serverside_blocks = -1;
protected boolean recipe$enable;
@@ -250,6 +251,7 @@ public class Config {
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "item.custom-model-data-starting-value.overrides", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "block.deceive-bukkit-material.overrides", '.')
.build());
}
try {
@@ -439,8 +441,25 @@ public class Config {
block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1);
block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0);
block$chunk_relighter = config.getBoolean("block.chunk-relighter", true);
block$deceive_bukkit_material = Key.of(config.getString("block.deceive-bukkit-material", "stone"));
if (firstTime) {
block$deceive_bukkit_material$default = Key.of(config.getString("block.deceive-bukkit-material.default", "bricks"));
block$deceive_bukkit_material$overrides = new HashMap<>();
Section overridesSection = config.getSection("block.deceive-bukkit-material.overrides");
if (overridesSection != null) {
for (Map.Entry<String, Object> entry : overridesSection.getStringRouteMappedValues(false).entrySet()) {
String key = entry.getKey();
Key value = Key.of(String.valueOf(entry.getValue()));
if (key.contains("~")) {
int min = Integer.parseInt(key.split("~")[0]);
int max = Integer.parseInt(key.split("~")[1]);
for (int i = min; i <= max; i++) {
block$deceive_bukkit_material$overrides.put(i, value);
}
} else {
block$deceive_bukkit_material$overrides.put(Integer.valueOf(key), value);
}
}
}
block$serverside_blocks = Math.min(config.getInt("block.serverside-blocks", 2000), 10_0000);
if (block$serverside_blocks < 0) block$serverside_blocks = 0;
}
@@ -774,8 +793,8 @@ public class Config {
return instance.resource_pack$protection$obfuscation$resource_location$bypass_equipments;
}
public static Key deceiveBukkitMaterial() {
return instance.block$deceive_bukkit_material;
public static Key deceiveBukkitMaterial(int id) {
return instance.block$deceive_bukkit_material$overrides.getOrDefault(id, instance.block$deceive_bukkit_material$default);
}
public static boolean generateModAssets() {

View File

@@ -11,6 +11,7 @@ public final class CommonConditions {
public static final Key ANY_OF = Key.of("craftengine:any_of");
public static final Key INVERTED = Key.of("craftengine:inverted");
public static final Key MATCH_ITEM = Key.of("craftengine:match_item");
public static final Key HAS_ITEM = Key.of("craftengine:has_item");
public static final Key MATCH_ENTITY = Key.of("craftengine:match_entity");
public static final Key MATCH_BLOCK = Key.of("craftengine:match_block");
public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property");

View File

@@ -0,0 +1,38 @@
package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.ItemUtils;
import net.momirealms.craftengine.core.util.Key;
import java.util.Map;
import java.util.Optional;
public class HasItemCondition<CTX extends Context> implements Condition<CTX> {
public HasItemCondition() {
}
@Override
public Key type() {
return CommonConditions.HAS_ITEM;
}
@Override
public boolean test(CTX ctx) {
Optional<Item<?>> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND);
if (item.isEmpty()) return false;
Item<?> itemInHand = item.get();
return !ItemUtils.isEmpty(itemInHand);
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
return new HasItemCondition<>();
}
}
}

View File

@@ -14,7 +14,7 @@ public class HasPlayerCondition<CTX extends Context> implements Condition<CTX> {
@Override
public Key type() {
return CommonConditions.IS_NULL;
return CommonConditions.HAS_PLAYER;
}
@Override

View File

@@ -35,7 +35,7 @@ public class MatchItemCondition<CTX extends Context> implements Condition<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
List<String> ids = MiscUtils.getAsStringList(arguments.get("id"));
List<String> ids = MiscUtils.getAsStringList(ResourceConfigUtils.get(arguments, "id", "item"));
if (ids.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.condition.match_item.missing_id");
}

View File

@@ -17,6 +17,7 @@ public class EventConditions {
static {
register(CommonConditions.HAS_PLAYER, new HasPlayerCondition.FactoryImpl<>());
register(CommonConditions.HAS_ITEM, new HasItemCondition.FactoryImpl<>());
register(CommonConditions.MATCH_ITEM, new MatchItemCondition.FactoryImpl<>());
register(CommonConditions.MATCH_ENTITY, new MatchEntityCondition.FactoryImpl<>());
register(CommonConditions.MATCH_BLOCK, new MatchBlockCondition.FactoryImpl<>());

View File

@@ -24,6 +24,8 @@ public class EventFunctions {
register(CommonFunctions.CANCEL_EVENT, new CancelEventFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.RUN, new RunFunction.FactoryImpl<>(EventFunctions::fromMap, EventConditions::fromMap));
register(CommonFunctions.PLACE_BLOCK, new PlaceBlockFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.UPDATE_BLOCK_PROPERTY, new UpdateBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.TRANSFORM_BLOCK, new TransformBlockFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.BREAK_BLOCK, new BreakBlockFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.UPDATE_INTERACTION_TICK, new UpdateInteractionFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.SET_COUNT, new SetCountFunction.FactoryImpl<>(EventConditions::fromMap));
@@ -48,6 +50,9 @@ public class EventFunctions {
register(CommonFunctions.DAMAGE, new DamageFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.MERCHANT_TRADE, new MerchantTradeFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.REMOVE_ENTITY, new RemoveEntityFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.IF_ELSE, new IfElseFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
register(CommonFunctions.ALTERNATIVES, new IfElseFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
}
public static void register(Key key, FunctionFactory<PlayerOptionalContext> factory) {

View File

@@ -28,29 +28,73 @@ public abstract class AbstractConditionalFunction<CTX extends Context> implement
protected abstract void runInternal(CTX ctx);
public static abstract class AbstractFactory<CTX extends Context> implements FunctionFactory<CTX> {
private final java.util.function.Function<Map<String, Object>, Condition<CTX>> factory;
protected final java.util.function.Function<Map<String, Object>, Condition<CTX>> conditionFactory;
public AbstractFactory(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
this.factory = factory;
public AbstractFactory(java.util.function.Function<Map<String, Object>, Condition<CTX>> conditionFactory) {
this.conditionFactory = conditionFactory;
}
public java.util.function.Function<Map<String, Object>, Condition<CTX>> conditionFactory() {
return factory;
return this.conditionFactory;
}
protected List<Condition<CTX>> getPredicates(Map<String, Object> arguments) {
if (arguments == null) return List.of();
Object predicates = arguments.get("conditions");
if (predicates == null) return List.of();
if (predicates instanceof List<?> list) {
switch (predicates) {
case List<?> list -> {
List<Condition<CTX>> conditions = new ArrayList<>(list.size());
for (Object o : list) {
conditions.add(factory.apply(MiscUtils.castToMap(o, false)));
conditions.add(this.conditionFactory.apply(MiscUtils.castToMap(o, false)));
}
return conditions;
} else if (predicates instanceof Map<?,?> map) {
return List.of(factory.apply(MiscUtils.castToMap(map, false)));
}
throw new UnsupportedOperationException("Unsupported conditions argument class type: " + predicates.getClass().getSimpleName());
case Map<?, ?> map -> {
return List.of(this.conditionFactory.apply(MiscUtils.castToMap(map, false)));
}
default -> {
return List.of();
}
}
}
}
public static abstract class AbstractFunctionalFactory<CTX extends Context> extends AbstractFactory<CTX> {
protected final java.util.function.Function<Map<String, Object>, Function<CTX>> functionFactory;
public AbstractFunctionalFactory(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory, java.util.function.Function<Map<String, Object>, Function<CTX>> functionFactory) {
super(factory);
this.functionFactory = functionFactory;
}
public java.util.function.Function<Map<String, Object>, Function<CTX>> functionFactory() {
return functionFactory;
}
protected List<Function<CTX>> getFunctions(Map<String, Object> arguments) {
if (arguments == null) return List.of();
Object functions = arguments.get("functions");
return parseFunctions(functions);
}
protected List<Function<CTX>> parseFunctions(Object functions) {
if (functions == null) return List.of();
switch (functions) {
case List<?> list -> {
List<Function<CTX>> conditions = new ArrayList<>(list.size());
for (Object o : list) {
conditions.add(this.functionFactory.apply(MiscUtils.castToMap(o, false)));
}
return conditions;
}
case Map<?, ?> map -> {
return List.of(this.functionFactory.apply(MiscUtils.castToMap(map, false)));
}
default -> {
return List.of();
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
import java.util.Collection;
public class AllOfFunction<CTX extends Context> implements Function<CTX> {
private final Function<CTX>[] functions;
public AllOfFunction(Function<CTX>[] functions) {
this.functions = functions;
}
@SuppressWarnings("unchecked")
public AllOfFunction(Collection<Function<CTX>> functions) {
this.functions = functions.toArray(new Function[0]);
}
@Override
public void run(CTX ctx) {
for (Function<CTX> function : this.functions) {
function.run(ctx);
}
}
@Override
public Key type() {
return CommonFunctions.ALL_OF;
}
}

View File

@@ -20,6 +20,8 @@ public final class CommonFunctions {
public static final Key UPDATE_INTERACTION_TICK = Key.of("craftengine:update_interaction_tick");
public static final Key SET_COUNT = Key.of("craftengine:set_count");
public static final Key PLACE_BLOCK = Key.of("craftengine:place_block");
public static final Key TRANSFORM_BLOCK = Key.of("craftengine:transform_block");
public static final Key UPDATE_BLOCK_PROPERTY = Key.of("craftengine:update_block_property");
public static final Key SET_FOOD = Key.of("craftengine:set_food");
public static final Key SET_COOLDOWN = Key.of("craftengine:set_cooldown");
public static final Key REMOVE_COOLDOWN = Key.of("craftengine:remove_cooldown");
@@ -37,4 +39,9 @@ public final class CommonFunctions {
public static final Key DAMAGE = Key.of("craftengine:damage");
public static final Key MERCHANT_TRADE = Key.of("craftengine:merchant_trade");
public static final Key REMOVE_ENTITY = Key.of("craftengine:remove_entity");
public static final Key IF_ELSE = Key.of("craftengine:if_else");
public static final Key ALTERNATIVES = Key.of("craftengine:alternatives");
public static final Key WHEN = Key.of("craftengine:when");
public static final Key ALL_OF = Key.of("craftengine:all_of");
public static final Key DUMMY = Key.of("craftengine:dummy");
}

View File

@@ -0,0 +1,16 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
public class DummyFunction<CTX extends Context> implements Function<CTX> {
@Override
public void run(CTX ctx) {
}
@Override
public Key type() {
return CommonFunctions.DUMMY;
}
}

View File

@@ -3,9 +3,32 @@ package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
public interface Function<CTX extends Context> {
void run(CTX ctx);
Key type();
static <CTX extends Context> Function<CTX> allOf(List<Function<CTX>> functions) {
if (functions == null || functions.isEmpty()) {
return new DummyFunction<>();
}
if (functions.size() == 1) {
return functions.getFirst();
}
return new AllOfFunction<>(functions);
}
@SafeVarargs
static <CTX extends Context> Function<CTX> allOf(Function<CTX>... functions) {
if (functions == null || functions.length == 0) {
return new DummyFunction<>();
}
if (functions.length == 1) {
return functions[0];
}
return new AllOfFunction<>(functions);
}
}

View File

@@ -0,0 +1,56 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.Pair;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public class IfElseFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final List<Pair<Predicate<CTX>, Function<CTX>>> conditions;
public IfElseFunction(List<Condition<CTX>> predicates, List<Pair<Predicate<CTX>, Function<CTX>>> conditions) {
super(predicates);
this.conditions = conditions;
}
@Override
public void runInternal(CTX ctx) {
for (Pair<Predicate<CTX>, Function<CTX>> condition : this.conditions) {
if (condition.left().test(ctx)) {
condition.right().run(ctx);
break;
}
}
}
@Override
public Key type() {
return CommonFunctions.IF_ELSE;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFunctionalFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> conditionFactory, java.util.function.Function<Map<String, Object>, Function<CTX>> functionFactory) {
super(conditionFactory, functionFactory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
List<Pair<Predicate<CTX>, Function<CTX>>> branches = ResourceConfigUtils.parseConfigAsList(
ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "rules", "rule"), "warning.config.function.if_else.missing_rules"),
map -> {
List<Condition<CTX>> conditions = getPredicates(map);
List<Function<CTX>> functions = getFunctions(map);
return new Pair<>(MiscUtils.allOf(conditions), Function.allOf(functions));
}
);
return new IfElseFunction<>(getPredicates(arguments), branches);
}
}
}

View File

@@ -58,11 +58,12 @@ public class PlaceBlockFunction<CTX extends Context> extends AbstractConditional
@Override
public Function<CTX> create(Map<String, Object> arguments) {
String state = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("block-state"), "warning.config.function.place_block.missing_block_state");
NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>"));
NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>"));
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
NumberProvider flags = Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags()));
return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)), x, y, z, flags, getPredicates(arguments));
return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)),
NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>")),
NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>")),
NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>")),
Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())),
getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,96 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.LazyReference;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.Tag;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
public class TransformBlockFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final LazyReference<BlockStateWrapper> lazyBlockState;
private final CompoundTag properties;
private final NumberProvider x;
private final NumberProvider y;
private final NumberProvider z;
private final NumberProvider updateFlags;
public TransformBlockFunction(LazyReference<BlockStateWrapper> lazyBlockState, CompoundTag properties, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, List<Condition<CTX>> predicates) {
super(predicates);
this.properties = properties;
this.x = x;
this.y = y;
this.z = z;
this.updateFlags = updateFlags;
this.lazyBlockState = lazyBlockState;
}
@Override
public void runInternal(CTX ctx) {
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalWorldPosition.isPresent()) {
World world = optionalWorldPosition.get().world();
int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
BlockStateWrapper existingBlockState = world.getBlockAt(x, y, z).blockState().withProperties(this.properties);
CompoundTag newProperties = new CompoundTag();
for (String propertyName : existingBlockState.getPropertyNames()) {
newProperties.putString(propertyName, String.valueOf(existingBlockState.getProperty(propertyName)).toLowerCase(Locale.ROOT));
}
if (!this.properties.isEmpty()) {
for (Map.Entry<String, Tag> tagEntry : this.properties.entrySet()) {
newProperties.put(tagEntry.getKey(), tagEntry.getValue());
}
}
world.setBlockAt(x, y, z, this.lazyBlockState.get().withProperties(newProperties), this.updateFlags.getInt(ctx));
}
}
@Override
public Key type() {
return CommonFunctions.TRANSFORM_BLOCK;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
super(factory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
String block = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("block"), "warning.config.function.transform_block.missing_block");
CompoundTag properties = new CompoundTag();
Map<String, Object> propertiesMap = MiscUtils.castToMap(arguments.get("properties"), true);
if (propertiesMap != null) {
for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
properties.putString(entry.getKey(), String.valueOf(entry.getValue()));
}
}
return new TransformBlockFunction<>(
LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(block)),
properties,
NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>")),
NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>")),
NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>")),
Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())),
getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,78 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.ExistingBlock;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class UpdateBlockPropertyFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final CompoundTag properties;
private final NumberProvider x;
private final NumberProvider y;
private final NumberProvider z;
private final NumberProvider updateFlags;
public UpdateBlockPropertyFunction(CompoundTag properties, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, List<Condition<CTX>> predicates) {
super(predicates);
this.properties = properties;
this.x = x;
this.y = y;
this.z = z;
this.updateFlags = updateFlags;
}
@Override
public void runInternal(CTX ctx) {
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalWorldPosition.isPresent()) {
World world = optionalWorldPosition.get().world();
int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
ExistingBlock blockAt = world.getBlockAt(x, y, z);
BlockStateWrapper wrapper = blockAt.blockState().withProperties(this.properties);
world.setBlockAt(x, y, z, wrapper, this.updateFlags.getInt(ctx));
}
}
@Override
public Key type() {
return CommonFunctions.UPDATE_BLOCK_PROPERTY;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
super(factory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
Map<String, Object> state = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("properties"), "warning.config.function.update_block_property.missing_properties"), "properties");
CompoundTag properties = new CompoundTag();
for (Map.Entry<String, Object> entry : state.entrySet()) {
properties.putString(entry.getKey(), String.valueOf(entry.getValue()));
}
return new UpdateBlockPropertyFunction<>(properties,
NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>")),
NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>")),
NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>")),
Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())),
getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,63 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.text.TextProvider;
import net.momirealms.craftengine.core.plugin.context.text.TextProviders;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.Pair;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WhenFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final TextProvider source;
private final Map<String, Function<CTX>> whenMap;
private final Function<CTX> fallback;
public WhenFunction(List<Condition<CTX>> predicates, TextProvider source, Map<String, Function<CTX>> whenMap, Function<CTX> fallback) {
super(predicates);
this.whenMap = whenMap;
this.source = source;
this.fallback = fallback;
}
@Override
public void runInternal(CTX ctx) {
String text = this.source.get(ctx);
Function<CTX> function = this.whenMap.getOrDefault(text, this.fallback);
function.run(ctx);
}
@Override
public Key type() {
return CommonFunctions.IF_ELSE;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFunctionalFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> conditionFactory, java.util.function.Function<Map<String, Object>, Function<CTX>> functionFactory) {
super(conditionFactory, functionFactory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
TextProvider source = TextProviders.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("source"), "warning.config.function.when.missing_source"));
List<Pair<List<String>, Function<CTX>>> list = ResourceConfigUtils.parseConfigAsList(arguments.get("cases"), map -> {
List<String> when = MiscUtils.getAsStringList(map.get("when"));
List<Function<CTX>> functions = getFunctions(map);
return Pair.of(when, Function.allOf(functions));
});
Map<String, Function<CTX>> whenMap = new HashMap<>();
for (Pair<List<String>, Function<CTX>> pair : list) {
for (String when : pair.left()) {
whenMap.put(when, pair.right());
}
}
return new WhenFunction<>(getPredicates(arguments), source, whenMap, Function.allOf(parseFunctions(arguments.get("fallback"))));
}
}
}

View File

@@ -18,8 +18,8 @@ public class TagTextProvider implements TextProvider {
@Override
public String get(Context context) {
Component resultComponent = AdventureHelper.customMiniMessage().deserialize(this.text, context.tagResolvers());
return AdventureHelper.plainTextContent(resultComponent);
Component resultComponent = AdventureHelper.miniMessage().deserialize(this.text, context.tagResolvers());
return AdventureHelper.strictMiniMessage().serialize(resultComponent);
}
@Override

View File

@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.64.19
config_version=49
lang_version=35
project_version=0.0.64.20
config_version=50
lang_version=36
project_group=net.momirealms
latest_supported_version=1.21.10