diff --git a/gradle.properties b/gradle.properties index 86ddea5..c5e718e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,14 +5,14 @@ org.gradle.jvmargs=-Xmx2G # Fabric Properties # check these on https://fabricmc.net/use minecraft_version=1.18.1 - yarn_mappings=1.18.1+build.2 - loader_version=0.12.11 + yarn_mappings=1.18.1+build.22 + loader_version=0.13.3 # Mod Properties - mod_version = 1.5.0 + mod_version = 1.5.1 maven_group = net.theelm archives_base_name = theelm-mod # Dependencies # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric - fabric_version=0.44.0+1.18 + fabric_version=0.46.4+1.18 diff --git a/src/main/java/net/TheElm/project/config/ConfigArray.java b/src/main/java/net/TheElm/project/config/ConfigArray.java index 8ff742f..7304e61 100644 --- a/src/main/java/net/TheElm/project/config/ConfigArray.java +++ b/src/main/java/net/TheElm/project/config/ConfigArray.java @@ -25,36 +25,67 @@ package net.TheElm.project.config; +import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import net.TheElm.project.interfaces.json.ConfigReader; +import net.TheElm.project.interfaces.json.ConfigWriter; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DefaultedRegistry; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadLocalRandom; -import java.util.function.Function; +import java.util.stream.Collectors; public final class ConfigArray extends ConfigBase { - private final Function setter; + private final ConfigReader setter; private final List value; + private @Nullable ConfigWriter serializer; - public ConfigArray(@NotNull String location, Function setter) { + public ConfigArray(@NotNull String location, ConfigReader setter) { this(location, new ArrayList<>(), setter); } - public ConfigArray(@NotNull String location, List defaultValue, Function setter) { + public ConfigArray(@NotNull String location, @NotNull List defaultValue, ConfigReader setter) { super(location); this.value = defaultValue; this.setter = setter; } + public ConfigArray(@NotNull String location, @NotNull List defaultValue, @NotNull ConfigReader setter, @Nullable ConfigWriter serializer) { + super(location); + + this.value = defaultValue; + this.setter = setter; + this.serializer = serializer; + } + + public ConfigArray serializer(ConfigWriter serializer) { + this.serializer = serializer; + return this; + } + + static JsonElement convertToJSON(@NotNull List list, @Nullable ConfigWriter serializer) { + Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .create(); + JsonArray array = new JsonArray(); + for (T el : list) { + array.add(serializer == null ? gson.toJsonTree(el) : serializer.serialize(el, gson)); + } + + return array; + } @Override JsonElement getElement() { - return new GsonBuilder() - .disableHtmlEscaping().create().toJsonTree(this.value); + return ConfigArray.convertToJSON(this.value, this.serializer); } List get() { return this.value; @@ -77,9 +108,9 @@ void set(JsonElement value) { // Add all values if (!(value instanceof JsonArray)) - this.value.add(this.setter.apply(value)); + this.value.add(this.setter.deserialize(value)); else for (JsonElement element : value.getAsJsonArray()) - this.value.add(this.setter.apply(element)); + this.value.add(this.setter.deserialize(element)); } public static @NotNull ConfigArray jInt(@NotNull String location) { @@ -94,4 +125,23 @@ else for (JsonElement element : value.getAsJsonArray()) public static @NotNull ConfigArray jString(@NotNull String location) { return new ConfigArray<>(location, JsonElement::getAsString); } + public static @NotNull ConfigArray> registry(String location, RegistryKey> registry, List> def) { + return new ConfigArray<>(location, def, (element) -> { + // Convert from String to RegistryKey + return RegistryKey.of(registry, new Identifier(element.getAsString())); + }, (type, gson) -> { + // Convert from RegistryKey to String + return gson.toJsonTree(type.getValue().toString()); + }); + } + public static @NotNull ConfigArray fromRegistry(String location, DefaultedRegistry registry, List defaults) { + List cast = defaults.stream().map((type) -> registry.get(registry.getId(type))).collect(Collectors.toList()); + return new ConfigArray<>(location, cast, (element) -> { + // Convert from String to RegistryKey + return registry.get(new Identifier(element.getAsString())); + }, (type, gson) -> { + // Convert from RegistryKey to String + return gson.toJsonTree(registry.getId(type).toString()); + }); + } } diff --git a/src/main/java/net/TheElm/project/config/ConfigOption.java b/src/main/java/net/TheElm/project/config/ConfigOption.java index ed99842..8f4e2c7 100644 --- a/src/main/java/net/TheElm/project/config/ConfigOption.java +++ b/src/main/java/net/TheElm/project/config/ConfigOption.java @@ -25,12 +25,14 @@ package net.TheElm.project.config; +import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializer; import net.TheElm.project.CoreMod; +import net.TheElm.project.interfaces.json.ConfigReader; +import net.TheElm.project.interfaces.json.ConfigWriter; import net.TheElm.project.objects.ChatFormat; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; @@ -40,21 +42,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.function.Function; - -public class ConfigOption extends ConfigBase { +public final class ConfigOption extends ConfigBase { - private final @NotNull Function setter; + private final @NotNull ConfigReader setter; private @Nullable T value; - private @Nullable JsonSerializer serializer; + private @Nullable ConfigWriter serializer; - public ConfigOption(@NotNull String location, @NotNull Function setter) { + public ConfigOption(@NotNull String location, @NotNull ConfigReader setter) { this(location, null, setter); } - public ConfigOption(@NotNull String location, @Nullable T defaultValue, @NotNull Function setter) { + public ConfigOption(@NotNull String location, @Nullable T defaultValue, @NotNull ConfigReader setter) { this(location, defaultValue, setter, null); } - public ConfigOption(@NotNull String location, @Nullable T defaultValue, @NotNull Function setter, @Nullable JsonSerializer serializer) { + public ConfigOption(@NotNull String location, @Nullable T defaultValue, @NotNull ConfigReader setter, @Nullable ConfigWriter serializer) { super(location); this.value = defaultValue; @@ -63,22 +63,22 @@ public ConfigOption(@NotNull String location, @Nullable T defaultValue, @NotNull } @Override - final void set( JsonElement value ) { - this.value = ( value == null ? null : this.setter.apply(value) ); + void set( JsonElement value ) { + this.value = ( value == null ? null : this.setter.deserialize(value) ); } - final void set( T value ) { + void set( T value ) { this.value = value; } - final T get() { + T get() { return this.value; } @Override - final JsonElement getElement() { + JsonElement getElement() { return ConfigOption.convertToJSON(this.value, this.serializer); } - public final ConfigOption serializer(JsonSerializer serializer) { + public ConfigOption serializer(ConfigWriter serializer) { this.serializer = serializer; return this; } @@ -86,11 +86,9 @@ public final ConfigOption serializer(JsonSerializer serializer) { public static JsonElement convertToJSON( @Nullable Object src ) { return ConfigOption.convertToJSON( src, null ); } - public static JsonElement convertToJSON(@Nullable T src, @Nullable JsonSerializer serializer ) { - GsonBuilder builder = new GsonBuilder(); - if (src != null && serializer != null) - builder.registerTypeAdapter(src.getClass(), serializer); - return builder.create().toJsonTree( src ); + static JsonElement convertToJSON(@Nullable T src, @Nullable ConfigWriter serializer) { + Gson gson = new GsonBuilder().create(); + return serializer == null ? gson.toJsonTree(src) : serializer.serialize(src, gson); } public static @NotNull ConfigOption chat(@NotNull String location, @NotNull String defaultFormat) { @@ -157,7 +155,7 @@ public static JsonElement convertToJSON(@Nullable T src, @Nullable JsonSeria CoreMod.logError(new Exception("Failed to parse BlockPos in config, using fallback value", ex)); } return BlockPos.ORIGIN; - }, (s, type, json) -> { + }, (s, json) -> { // Create an array JsonArray array = new JsonArray(); @@ -169,19 +167,19 @@ public static JsonElement convertToJSON(@Nullable T src, @Nullable JsonSeria return array; }); } - public static @NotNull > ConfigOption comparable(@NotNull String location, @Nullable T def, final @NotNull T minimum, final @NotNull T maximum, final @NotNull Function setter ) { + public static @NotNull > ConfigOption comparable(@NotNull String location, @Nullable T def, final @NotNull T minimum, final @NotNull T maximum, final @NotNull ConfigReader setter ) { return new ConfigOption<>(location, def, (raw) -> { - T setTo = setter.apply(raw); + T setTo = setter.deserialize(raw); return (setTo.compareTo(minimum) < 0 || maximum.compareTo(setTo) < 0) ? def : setTo; }); } - public static @NotNull ConfigOption> registry(String location, RegistryKey def, RegistryKey> registry) { + public static @NotNull ConfigOption> registry(String location, RegistryKey> registry, RegistryKey def) { return new ConfigOption<>(location, def, (element) -> { // Convert from String to RegistryKey return RegistryKey.of(registry, new Identifier(element.getAsString())); - }, (world, type, json) -> { + }, (type, json) -> { // Convert from RegistryKey to String - return json.serialize(world.getValue().toString()); + return json.toJsonTree(type.getValue().toString()); }); } } diff --git a/src/main/java/net/TheElm/project/config/SewConfig.java b/src/main/java/net/TheElm/project/config/SewConfig.java index 0c5e01c..4c81c92 100644 --- a/src/main/java/net/TheElm/project/config/SewConfig.java +++ b/src/main/java/net/TheElm/project/config/SewConfig.java @@ -28,14 +28,19 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import net.TheElm.project.CoreMod; import net.TheElm.project.config.addons.SewBluemapConfig; import net.TheElm.project.objects.ChatFormat; import net.TheElm.project.protections.logging.EventLogger.LoggingIntervals; import net.TheElm.project.utilities.DevUtils; +import net.TheElm.project.utilities.EntityUtils; import net.TheElm.project.utilities.FormattingUtils; +import net.TheElm.project.utilities.IntUtils; +import net.minecraft.entity.EntityType; import net.minecraft.item.Item; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; @@ -51,14 +56,18 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public final class SewConfig extends SewConfigContainer { private static final SewConfig INSTANCE = new SewConfig(); @@ -76,7 +85,7 @@ public final class SewConfig extends SewConfigContainer { public static final ConfigOption CONFIG_VERSION = SewConfig.addConfig(ConfigOption.json(SewConfig.VERSION_KEY, CoreMod.getModVersion())); // Database - public static final ConfigOption DB_LITE = /*SewConfig.addConfig(*/ConfigOption.json("database.sqlite", true)/*)*/; + public static final ConfigOption DB_LITE = /*SewConfig.addConfig(*/ConfigOption.json("database.sqlite", false)/*)*/; /* * Database handling @@ -234,7 +243,7 @@ public final class SewConfig extends SewConfigContainer { * Worlds */ - public static final ConfigOption> DEFAULT_WORLD = SewConfig.addConfig(ConfigOption.registry("server.worlds.spawn_world", World.OVERWORLD, Registry.WORLD_KEY)); + public static final ConfigOption> DEFAULT_WORLD = SewConfig.addConfig(ConfigOption.registry("server.worlds.spawn_world", Registry.WORLD_KEY, World.OVERWORLD)); public static final ConfigOption WORLD_DIMENSION_FOLDERS = SewConfig.addConfig(ConfigOption.json("server.worlds.use_dimension_folder", false)); public static final Supplier WORLD_SEPARATE_PROPERTIES = () -> SewConfig.get(SewConfig.WORLD_SPECIFIC_TIME) @@ -254,7 +263,7 @@ public final class SewConfig extends SewConfigContainer { * Warping */ - public static final ConfigOption> WARP_DIMENSION = SewConfig.addConfig(ConfigOption.registry("warp.world", World.OVERWORLD, Registry.WORLD_KEY)); + public static final ConfigOption> WARP_DIMENSION = SewConfig.addConfig(ConfigOption.registry("warp.world", Registry.WORLD_KEY, World.OVERWORLD)); public static final ConfigOption WARP_MAX_DISTANCE = SewConfig.addConfig(ConfigOption.json("warp.max_distance", 1000000)); public static final ConfigOption WARP_WAYSTONE_COST = SewConfig.addConfig(ConfigOption.json("warp.waystone.cost", 2000)); public static final ConfigOption WARP_WAYSTONES_ALLOWED = SewConfig.addConfig(ConfigOption.json("warp.waystone.maximum", 3)); @@ -295,6 +304,8 @@ public final class SewConfig extends SewConfigContainer { * Miscellaneous */ + public static final ConfigOption> ITEM_DESPAWN_TIMES = SewConfig.addConfig(new ConfigOption<>("server.items.despawn", new HashMap<>(), SewConfig::getItemDespawnMap)); + public static final ConfigOption OVERWORLD_PORTAL_LOC = SewConfig.addConfig(ConfigOption.json("fun.world.portal_fix.overworld", false)); public static final ConfigOption NETHER_PORTAL_LOC = SewConfig.addConfig(ConfigOption.json("fun.world.portal_fix.nether", true)); @@ -320,7 +331,7 @@ public final class SewConfig extends SewConfigContainer { public static final ConfigOption WANDERING_TRADER_CAMPFIRES = SewConfig.addConfig(ConfigOption.json("fun.mobs.wandering_trader.toggle_campfires", false)); public static final ConfigOption WANDERING_TRADER_HITCH = SewConfig.addConfig(ConfigOption.json("fun.mobs.wandering_trader.hitch_llamas", false)); public static final ConfigOption WANDERING_TRADER_FORCE_SPAWN = SewConfig.addConfig(ConfigOption.json("fun.mobs.wandering_trader.force_spawn.enable", false)); - public static final ConfigOption> WANDERING_TRADER_FORCE_SPAWN_WORLD = SewConfig.addConfig(ConfigOption.registry("fun.mobs.wandering_trader.force_spawn.world", World.OVERWORLD, Registry.WORLD_KEY)); + public static final ConfigOption> WANDERING_TRADER_FORCE_SPAWN_WORLD = SewConfig.addConfig(ConfigOption.registry("fun.mobs.wandering_trader.force_spawn.world", Registry.WORLD_KEY, World.OVERWORLD)); public static final ConfigOption WANDERING_TRADER_FORCE_SPAWN_POS = SewConfig.addConfig(ConfigOption.blockPos("fun.mobs.wandering_trader.force_spawn.pos", BlockPos.ZERO)); public static final ConfigOption WOLF_DAMAGE_RESIST = SewConfig.addConfig(ConfigOption.json("fun.mobs.wolf.buffs.resistance_multiplier", 3)); @@ -340,6 +351,7 @@ public final class SewConfig extends SewConfigContainer { public static final ConfigOption SILK_TOUCH_SPAWNERS = SewConfig.addConfig(ConfigOption.json("fun.spawners.silk_touch", false)); public static final ConfigOption SPAWNER_PICKUP_DAMAGE = SewConfig.addConfig(ConfigOption.json("fun.spawners.silk_touch_damage", 390)); public static final ConfigOption SPAWNER_ABSORB_MOBS = SewConfig.addConfig(ConfigOption.json("fun.spawners.absorb_mob_souls", true)); + public static final ConfigArray> SPAWNER_ABSORB_BLACKLIST = SewConfig.addConfig(ConfigArray.fromRegistry("fun.spawners.mob_blacklist", Registry.ENTITY_TYPE, Arrays.asList(EntityType.IRON_GOLEM, EntityType.VILLAGER, EntityType.WANDERING_TRADER, EntityType.WITHER, EntityType.ELDER_GUARDIAN, EntityType.ENDER_DRAGON))); static { File config = null; @@ -533,21 +545,58 @@ public static void afterReload(Runnable runnable) { /* * Special handlers */ - private static Map getItemMap(JsonElement element) { + private static Map getItemMap(JsonElement root) { Map out = new HashMap<>(); // Parse each object in the array - JsonObject list = element.getAsJsonObject(); + JsonObject list = root.getAsJsonObject(); for ( Map.Entry row : list.entrySet() ) { String token = row.getKey(); + Optional optional = Registry.ITEM.getOrEmpty(new Identifier(token)); + int count = row.getValue().getAsInt(); + optional.ifPresentOrElse( + item -> out.put(item, count), + () -> CoreMod.logError("Unable to find starting item \"" + token + "\" in the item registry.") + ); + } + + return out; + } + + private static Map getItemDespawnMap(JsonElement root) { + Map out = new HashMap<>(); + Pattern pattern = Pattern.compile("^([0-9]+)([smhd])$"); + + // Parse each object in the array + JsonObject list = root.getAsJsonObject(); + for ( Map.Entry row : list.entrySet() ) { + String token = row.getKey(); + Optional optional = Registry.ITEM.getOrEmpty(new Identifier(token)); - if (count > 0) { - out.put( - Registry.ITEM.get(new Identifier(token)), - count - ); - } + JsonElement element = row.getValue(); + + optional.ifPresentOrElse(item -> { + Integer ticks = EntityUtils.DEFAULT_DESPAWN_TICKS; + if (element instanceof JsonNull) + ticks = null; + else if (element instanceof JsonPrimitive primitive) { + if (primitive.isNumber()) + ticks = primitive.getAsInt(); + else if (primitive.isString()) { + String string = primitive.getAsString(); + Matcher matcher = pattern.matcher(string); + if (!matcher.matches()) + ticks = primitive.getAsInt(); + else { + int duration = Integer.parseInt(matcher.group(1)); + ticks = IntUtils.convertToTicks(duration, IntUtils.getTimeUnit(matcher.group(2))); + } + } + } + + out.put(item, ticks); + }, () -> CoreMod.logError("")); } return out; diff --git a/src/main/java/net/TheElm/project/interfaces/SpawnerMob.java b/src/main/java/net/TheElm/project/interfaces/SpawnerMob.java new file mode 100644 index 0000000..e317f4f --- /dev/null +++ b/src/main/java/net/TheElm/project/interfaces/SpawnerMob.java @@ -0,0 +1,34 @@ +/* + * This software is licensed under the MIT License + * https://github.com/GStefanowich/MC-Server-Protection + * + * Copyright (c) 2019 Gregory Stefanowich + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.TheElm.project.interfaces; + +/** + * Created on Jan 02 2022 at 4:51 PM. + * By greg in SewingMachineMod + */ +public interface SpawnerMob { + boolean checkIfFromSpawner(); +} diff --git a/src/main/java/net/TheElm/project/interfaces/json/ConfigReader.java b/src/main/java/net/TheElm/project/interfaces/json/ConfigReader.java new file mode 100644 index 0000000..5e2e16b --- /dev/null +++ b/src/main/java/net/TheElm/project/interfaces/json/ConfigReader.java @@ -0,0 +1,37 @@ +/* + * This software is licensed under the MIT License + * https://github.com/GStefanowich/MC-Server-Protection + * + * Copyright (c) 2019 Gregory Stefanowich + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.TheElm.project.interfaces.json; + +import com.google.gson.JsonElement; + +/** + * Created on Jan 02 2022 at 5:57 PM. + * By greg in SewingMachineMod + */ +@FunctionalInterface +public interface ConfigReader { + T deserialize(JsonElement json); +} diff --git a/src/main/java/net/TheElm/project/interfaces/json/ConfigWriter.java b/src/main/java/net/TheElm/project/interfaces/json/ConfigWriter.java new file mode 100644 index 0000000..69ea310 --- /dev/null +++ b/src/main/java/net/TheElm/project/interfaces/json/ConfigWriter.java @@ -0,0 +1,38 @@ +/* + * This software is licensed under the MIT License + * https://github.com/GStefanowich/MC-Server-Protection + * + * Copyright (c) 2019 Gregory Stefanowich + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.TheElm.project.interfaces.json; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +/** + * Created on Jan 02 2022 at 5:57 PM. + * By greg in SewingMachineMod + */ +@FunctionalInterface +public interface ConfigWriter { + JsonElement serialize(T type, Gson gson); +} diff --git a/src/main/java/net/TheElm/project/mixins/Commands/WorldBorder.java b/src/main/java/net/TheElm/project/mixins/Commands/WorldBorder.java new file mode 100644 index 0000000..b2062f1 --- /dev/null +++ b/src/main/java/net/TheElm/project/mixins/Commands/WorldBorder.java @@ -0,0 +1,84 @@ +/* + * This software is licensed under the MIT License + * https://github.com/GStefanowich/MC-Server-Protection + * + * Copyright (c) 2019 Gregory Stefanowich + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.TheElm.project.mixins.Commands; + +import net.TheElm.project.config.SewConfig; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.command.WorldBorderCommand; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.Vec2f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * Created on Dec 21 2021 at 1:33 PM. + * By greg in SewingMachineMod + */ +@Mixin(WorldBorderCommand.class) +public class WorldBorder { + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeBuffer") + private static ServerWorld onExecuteBuffer(MinecraftServer server, ServerCommandSource source, float distance) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeDamage") + private static ServerWorld onExecuteDamage(MinecraftServer server, ServerCommandSource source, float damagePerBlock) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeWarningTime") + private static ServerWorld onExecuteWarningTime(MinecraftServer server, ServerCommandSource source, int time) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeWarningDistance") + private static ServerWorld onExecuteWarningDistance(MinecraftServer server, ServerCommandSource source, int distance) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeGet") + private static ServerWorld onExecuteGet(MinecraftServer server, ServerCommandSource source) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeCenter") + private static ServerWorld onExecuteCenter(MinecraftServer server, ServerCommandSource source, Vec2f pos) { + return WorldBorder.getWorld(server, source); + } + + @Redirect(at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.getOverworld()Lnet/minecraft/server/world/ServerWorld;"), method = "executeSet") + private static ServerWorld onExecuteSet(MinecraftServer server, ServerCommandSource source, double distance, long time) { + return WorldBorder.getWorld(server, source); + } + + private static ServerWorld getWorld(MinecraftServer server, ServerCommandSource source) { + if (SewConfig.get(SewConfig.WORLD_SPECIFIC_WORLD_BORDER)) + return source.getWorld(); + return server.getOverworld(); + } +} diff --git a/src/main/java/net/TheElm/project/mixins/Entities/Death.java b/src/main/java/net/TheElm/project/mixins/Entities/Death.java index e59894d..5451be3 100644 --- a/src/main/java/net/TheElm/project/mixins/Entities/Death.java +++ b/src/main/java/net/TheElm/project/mixins/Entities/Death.java @@ -39,7 +39,7 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; -import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.Monster; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; @@ -63,7 +63,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(LivingEntity.class) public abstract class Death extends Entity { @@ -104,11 +103,11 @@ public void onDeath(DamageSource damageSource, CallbackInfo callback) { return; // Check if mob type is allowed to be spawned - EntityType type = this.getType(); - if (!EntityUtils.canBeSpawnered(type)) + if (!EntityUtils.canBeSpawnered(this)) return; // Get the identifier of the mob we killed + EntityType type = this.getType(); NbtString mobId = NbtString.of(EntityType.getId(type).toString()); // Get current entity IDs @@ -240,7 +239,7 @@ protected boolean onDamage(@NotNull LivingEntity self, @NotNull DamageSource sou } // If the player can fall from any worlds void back to spawn - if (SewConfig.get(SewConfig.VOID_FALL_TO_SPAWN) && !(self instanceof HostileEntity)) { // Teleport to the spawn world + if (SewConfig.get(SewConfig.VOID_FALL_TO_SPAWN) && !(self instanceof Monster)) { // Teleport to the spawn world WarpUtils.teleportEntity(ServerCore.defaultWorldKey(), this); return false; } diff --git a/src/main/java/net/TheElm/project/mixins/Entities/WitherBoss.java b/src/main/java/net/TheElm/project/mixins/Entities/WitherBoss.java index c3438e7..44d58a9 100644 --- a/src/main/java/net/TheElm/project/mixins/Entities/WitherBoss.java +++ b/src/main/java/net/TheElm/project/mixins/Entities/WitherBoss.java @@ -70,13 +70,13 @@ public void setEntityOwner(UUID owner) { } @Inject(at = @At("TAIL"), method = "writeCustomDataToNbt") - public void onSavingData(NbtCompound tag, CallbackInfo callback) { + public void onSavingData(@NotNull NbtCompound tag, @NotNull CallbackInfo callback) { // Save the players money if (this.getEntityOwner() != null) tag.putUuid("WitherCreator", this.getEntityOwner()); } @Inject(at = @At("TAIL"), method = "readCustomDataFromNbt") - public void onReadingData(NbtCompound tag, CallbackInfo callback) { + public void onReadingData(@NotNull NbtCompound tag, @NotNull CallbackInfo callback) { // Read the players money if (NbtUtils.hasUUID(tag, "WitherCreator")) this.setEntityOwner(NbtUtils.getUUID(tag, "WitherCreator")); diff --git a/src/main/java/net/TheElm/project/mixins/Player/DeathChest.java b/src/main/java/net/TheElm/project/mixins/Player/DeathChest.java index 89c25d6..5e35ffd 100644 --- a/src/main/java/net/TheElm/project/mixins/Player/DeathChest.java +++ b/src/main/java/net/TheElm/project/mixins/Player/DeathChest.java @@ -94,7 +94,10 @@ public void onInventoryDrop(CallbackInfo callback) { // Drop the backpack if we're not using death chests (And keep inventory is off) if (!keepInventory) { DeathChestUtils.createDeathSnapshotFor((PlayerEntity)(LivingEntity) this); - this.backpack.dropAll(true); + + // Drop the contents of the backpack (Only if the player HAS one) + if (this.backpack != null) + this.backpack.dropAll(true); } return; } @@ -106,8 +109,9 @@ public void onInventoryDrop(CallbackInfo callback) { // Check if player is in combat if (SewConfig.get(SewConfig.PVP_DISABLE_DEATH_CHEST) && (this.hitByOtherPlayerAt != null)) { - // Drop the backpack as well as the inventory - this.backpack.dropAll(true); + // Drop the backpack as well as the inventory (Only if the player HAS one) + if (this.backpack != null) + this.backpack.dropAll(true); // Tell the player that they didn't get a death chest this.sendSystemMessage( @@ -121,7 +125,7 @@ public void onInventoryDrop(CallbackInfo callback) { } // If the inventory is NOT empty, and we found a valid position for the death chest - if ((!(InventoryUtils.isInvEmpty(this.inventory) && InventoryUtils.isInvEmpty(this.backpack))) && ((chestPos = DeathChestUtils.getChestPosition( this.getEntityWorld(), this.getBlockPos() )) != null)) { + if ((!(InventoryUtils.isInvEmpty(this.inventory) && InventoryUtils.isInvEmpty(this.backpack))) && ((chestPos = DeathChestUtils.getChestPosition(this.getEntityWorld(), this.getBlockPos() )) != null)) { // Vanish cursed items this.vanishCursedItems(); diff --git a/src/main/java/net/TheElm/project/mixins/Player/Interaction/ArmorStand.java b/src/main/java/net/TheElm/project/mixins/Player/Interaction/ArmorStand.java index efe1ba8..232c6fe 100644 --- a/src/main/java/net/TheElm/project/mixins/Player/Interaction/ArmorStand.java +++ b/src/main/java/net/TheElm/project/mixins/Player/Interaction/ArmorStand.java @@ -103,8 +103,8 @@ private void onDropSelf(World world, BlockPos blockPos, ItemStack itemStack) { boolean hidePlate = this.shouldHideBasePlate(); if (showArms || hidePlate) { - NbtCompound name = itemStack.getSubNbt("display"); - NbtCompound enti = itemStack.getSubNbt("EntityTag"); + NbtCompound name = itemStack.getOrCreateSubNbt("display"); + NbtCompound enti = itemStack.getOrCreateSubNbt("EntityTag"); enti.putBoolean("ShowArms", showArms); enti.putBoolean("NoBasePlate", hidePlate); diff --git a/src/main/java/net/TheElm/project/mixins/Player/Interaction/ItemPickup.java b/src/main/java/net/TheElm/project/mixins/Player/Interaction/ItemPickup.java index f6153d9..9c2de59 100644 --- a/src/main/java/net/TheElm/project/mixins/Player/Interaction/ItemPickup.java +++ b/src/main/java/net/TheElm/project/mixins/Player/Interaction/ItemPickup.java @@ -27,11 +27,13 @@ import net.TheElm.project.config.SewConfig; import net.TheElm.project.utilities.ChunkUtils; +import net.TheElm.project.utilities.EntityUtils; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.sound.SoundCategory; @@ -41,10 +43,13 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Map; import java.util.UUID; @Mixin(ItemEntity.class) @@ -54,10 +59,18 @@ public abstract class ItemPickup extends Entity { @Shadow private UUID thrower; @Shadow private UUID owner; + private Integer overriddenSewingDespawnTime = null; + public ItemPickup(EntityType entityType_1, World world_1) { super(entityType_1, world_1); } + @Inject(at = @At("HEAD"), method = "setStack") + private void OnConstruct(ItemStack stack, CallbackInfo callback) { + Map items = SewConfig.get(SewConfig.ITEM_DESPAWN_TIMES); + this.overriddenSewingDespawnTime = items.get(stack.getItem()); + } + @Inject(at = @At("HEAD"), method = "onPlayerCollision", cancellable = true) public void attemptPickup(PlayerEntity player, CallbackInfo callback) { if (!this.world.isClient) { @@ -85,4 +98,10 @@ public void onDamage(DamageSource damageSource, float damage, CallbackInfoReturn } } + @ModifyConstant(method = "tick", constant = @Constant(intValue = 6000)) + private int anvilMaxLevelOverride(int oldValue) { + if (this.overriddenSewingDespawnTime != null) + return this.overriddenSewingDespawnTime; + return oldValue; + } } diff --git a/src/main/java/net/TheElm/project/mixins/Player/Interaction/TamedLead.java b/src/main/java/net/TheElm/project/mixins/Player/Interaction/TamedLead.java index d737046..603489a 100644 --- a/src/main/java/net/TheElm/project/mixins/Player/Interaction/TamedLead.java +++ b/src/main/java/net/TheElm/project/mixins/Player/Interaction/TamedLead.java @@ -27,14 +27,17 @@ import net.TheElm.project.config.SewConfig; import net.TheElm.project.interfaces.IClaimedChunk; +import net.TheElm.project.interfaces.SpawnerMob; import net.TheElm.project.protections.claiming.ClaimantPlayer; import net.TheElm.project.utilities.ChunkUtils; import net.TheElm.project.utilities.EntityUtils; import net.TheElm.project.utilities.TitleUtils; import net.TheElm.project.utilities.TranslatableServerSide; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityData; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.SpawnReason; import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.entity.passive.HorseBaseEntity; @@ -43,6 +46,8 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; import net.minecraft.network.packet.s2c.play.EntityAttachS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; @@ -50,6 +55,8 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Formatting; import net.minecraft.util.Hand; +import net.minecraft.world.LocalDifficulty; +import net.minecraft.world.ServerWorldAccess; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.jetbrains.annotations.NotNull; @@ -57,12 +64,15 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.UUID; @Mixin(MobEntity.class) -public abstract class TamedLead extends LivingEntity { +public abstract class TamedLead extends LivingEntity implements SpawnerMob { + + private boolean wasFromSpawner = false; protected TamedLead(EntityType entityType_1, World world_1) { super(entityType_1, world_1); @@ -77,6 +87,11 @@ private UUID getOwnerUuid() { return null; } + @Override + public boolean checkIfFromSpawner() { + return this.wasFromSpawner; + } + /** * Control attaching of leashes to this mob * @param player The entity to attach to @@ -84,7 +99,7 @@ private UUID getOwnerUuid() { * @param callback The callback */ @Inject(at = @At("HEAD"), method = "interactWithItem", cancellable = true) - private void attachLeash(@NotNull final PlayerEntity player, @NotNull final Hand hand, @NotNull final CallbackInfoReturnable callback) { + private void onAttachLeash(@NotNull final PlayerEntity player, @NotNull final Hand hand, @NotNull final CallbackInfoReturnable callback) { // If player is in creative mode, bypass permissions if ((player.isCreative() && SewConfig.get(SewConfig.CLAIM_CREATIVE_BYPASS)) || player.isSpectator()) return; @@ -150,7 +165,7 @@ private void attachLeash(@NotNull final PlayerEntity player, @NotNull final Hand * @param callback The callback */ @Inject(at = @At("HEAD"), method = "interactMob", cancellable = true) - private void interactMob(@NotNull PlayerEntity player, @NotNull Hand hand, @NotNull final CallbackInfoReturnable callback) { + private void onInteractMob(@NotNull PlayerEntity player, @NotNull Hand hand, @NotNull final CallbackInfoReturnable callback) { // If the player is not sneaking or is holding an item if (!player.isSneaking() || !player.getStackInHand(hand).isEmpty()) return; @@ -167,4 +182,33 @@ private void interactMob(@NotNull PlayerEntity player, @NotNull Hand hand, @NotN callback.setReturnValue(ActionResult.SUCCESS); } } + + /** + * When the mob is initialized + * We want to know if it is from a spawner + * @param world World spawned in + * @param difficulty Area difficulty + * @param spawnReason Cause of spawning + * @param entityData Current entity data + * @param entityNbt Current entity nbt + * @param callback Return type + */ + @Inject(at = @At("HEAD"), method = "initialize") + private void onInitializeMob(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @Nullable EntityData entityData, @Nullable NbtCompound entityNbt, CallbackInfoReturnable callback) { + if (spawnReason == SpawnReason.SPAWNER || spawnReason == SpawnReason.SPAWN_EGG) + this.wasFromSpawner = true; + } + + @Inject(at = @At("TAIL"), method = "writeCustomDataToNbt") + public void onSavingData(@NotNull NbtCompound tag, @NotNull CallbackInfo callback) { + // Save the players money + if (this.checkIfFromSpawner()) + tag.putBoolean("FromSpawner", this.checkIfFromSpawner()); + } + @Inject(at = @At("TAIL"), method = "readCustomDataFromNbt") + public void onReadingData(@NotNull NbtCompound tag, @NotNull CallbackInfo callback) { + // Read the players money + if (tag.contains("FromSpawner", NbtElement.NUMBER_TYPE)) + this.wasFromSpawner = tag.getBoolean("FromSpawner"); + } } diff --git a/src/main/java/net/TheElm/project/mixins/World/CauldronCleaning.java b/src/main/java/net/TheElm/project/mixins/World/CauldronCleaning.java index 23354f6..b7f6784 100644 --- a/src/main/java/net/TheElm/project/mixins/World/CauldronCleaning.java +++ b/src/main/java/net/TheElm/project/mixins/World/CauldronCleaning.java @@ -56,9 +56,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import org.jetbrains.annotations.NotNull; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -67,7 +65,6 @@ import java.util.List; import java.util.UUID; -import java.util.function.Predicate; @Mixin(LeveledCauldronBlock.class) public abstract class CauldronCleaning extends Block { diff --git a/src/main/java/net/TheElm/project/objects/ChatFormat.java b/src/main/java/net/TheElm/project/objects/ChatFormat.java index ad9033e..fe368a9 100644 --- a/src/main/java/net/TheElm/project/objects/ChatFormat.java +++ b/src/main/java/net/TheElm/project/objects/ChatFormat.java @@ -25,8 +25,8 @@ package net.TheElm.project.objects; +import com.google.gson.Gson; import com.google.gson.JsonElement; -import com.google.gson.JsonSerializationContext; import net.TheElm.project.CoreMod; import net.TheElm.project.enums.ChatRooms; import net.TheElm.project.interfaces.chat.ChatFunction; @@ -41,7 +41,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Type; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -75,10 +74,10 @@ public String toString() { public static @NotNull ChatFormat parse(@NotNull JsonElement config) { return ChatFormat.parse(config.getAsString()); } - public static JsonElement serializer(@NotNull ChatFormat src, Type type, @NotNull JsonSerializationContext context) { - return context.serialize(src.toString()); + public static JsonElement serializer(@NotNull ChatFormat src, @NotNull Gson gson) { + return gson.toJsonTree(src.toString()); } - private static @Contract("null, _, _, _ -> null") String replaceVariables( + private static @Contract("_, null, _, _, _ -> null") String replaceVariables( @NotNull final MutableText text, @Nullable final String segment, @NotNull final ServerCommandSource source, diff --git a/src/main/java/net/TheElm/project/protections/events/EntityAttack.java b/src/main/java/net/TheElm/project/protections/events/EntityAttack.java index 9100147..e801bb1 100644 --- a/src/main/java/net/TheElm/project/protections/events/EntityAttack.java +++ b/src/main/java/net/TheElm/project/protections/events/EntityAttack.java @@ -44,7 +44,7 @@ import net.minecraft.entity.decoration.ItemFrameEntity; import net.minecraft.entity.mob.AbstractPiglinEntity; import net.minecraft.entity.mob.CreeperEntity; -import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.Monster; import net.minecraft.entity.passive.TameableEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.ProjectileEntity; @@ -92,7 +92,7 @@ private static ActionResult attack(@NotNull final Entity target, @NotNull final if (attacker instanceof PlayerEntity player) { // Always allow defending self from hostiles - if (target instanceof HostileEntity && (!(target instanceof AbstractPiglinEntity abstractPiglin) || abstractPiglin.getTarget() instanceof PlayerEntity)) + if ((target instanceof Monster) && (!(target instanceof AbstractPiglinEntity abstractPiglin) || abstractPiglin.getTarget() instanceof PlayerEntity)) return ActionResult.PASS; // Do special item frame interaction if NOT CROUCHING and HOLDING A TOOL diff --git a/src/main/java/net/TheElm/project/utilities/DimensionUtils.java b/src/main/java/net/TheElm/project/utilities/DimensionUtils.java index e0d165f..96e4e77 100644 --- a/src/main/java/net/TheElm/project/utilities/DimensionUtils.java +++ b/src/main/java/net/TheElm/project/utilities/DimensionUtils.java @@ -34,7 +34,6 @@ import net.minecraft.network.packet.s2c.play.WorldBorderWarningBlocksChangedS2CPacket; import net.minecraft.network.packet.s2c.play.WorldBorderWarningTimeChangedS2CPacket; import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.LiteralText; import net.minecraft.text.MutableText; import net.minecraft.util.Formatting; import net.minecraft.util.math.BlockPos; diff --git a/src/main/java/net/TheElm/project/utilities/EntityUtils.java b/src/main/java/net/TheElm/project/utilities/EntityUtils.java index a4f147d..a2375d0 100644 --- a/src/main/java/net/TheElm/project/utilities/EntityUtils.java +++ b/src/main/java/net/TheElm/project/utilities/EntityUtils.java @@ -27,9 +27,11 @@ import net.TheElm.project.CoreMod; import net.TheElm.project.ServerCore; +import net.TheElm.project.config.SewConfig; import net.TheElm.project.enums.ClaimPermissions; import net.TheElm.project.interfaces.IClaimedChunk; import net.TheElm.project.interfaces.ShopSignData; +import net.TheElm.project.interfaces.SpawnerMob; import net.TheElm.project.mixins.Server.ServerWorldAccessor; import net.TheElm.project.protections.claiming.ClaimantTown; import net.TheElm.project.utilities.text.MessageUtils; @@ -140,6 +142,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Random; import java.util.Set; @@ -148,6 +151,7 @@ import java.util.stream.Stream; public final class EntityUtils { + public static final int DEFAULT_DESPAWN_TICKS = 6000; private EntityUtils() {} @@ -439,19 +443,29 @@ public static void updateHealthBar(@NotNull LivingEntity entity, @NotNull BossBa /* * Mob spawners */ + public static boolean canBeSpawnered(@NotNull Entity entity) { + if (entity instanceof SpawnerMob spawnerMob && spawnerMob.checkIfFromSpawner()) + return false; + return EntityUtils.canBeSpawnered(entity.getType()); + } public static boolean canBeSpawnered(@NotNull EntityType type) { return type.isSummonable() && !EntityUtils.preventSpawnered(type); } public static boolean preventSpawnered(@NotNull EntityType type) { - return type == EntityType.WANDERING_TRADER - || type == EntityType.IRON_GOLEM - || EntityUtils.isBossEntity(type); + if (EntityUtils.cannotBeSpawnered(type)) + return true; + List> blacklist = SewConfig.get(SewConfig.SPAWNER_ABSORB_BLACKLIST); + for (EntityType blacklisted : blacklist) { + if (Objects.equals(blacklisted, type)) + return true; + } + + return false; } - public static boolean isBossEntity(@NotNull EntityType type) { + public static boolean cannotBeSpawnered(@NotNull EntityType type) { return type == EntityType.ELDER_GUARDIAN - || type == EntityType.WITHER || type == EntityType.WITHER_SKULL - || type == EntityType.ENDER_DRAGON; + || type == EntityType.DRAGON_FIREBALL; } /* diff --git a/src/main/java/net/TheElm/project/utilities/IntUtils.java b/src/main/java/net/TheElm/project/utilities/IntUtils.java index 207cb3b..22955d4 100644 --- a/src/main/java/net/TheElm/project/utilities/IntUtils.java +++ b/src/main/java/net/TheElm/project/utilities/IntUtils.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Random; +import java.util.concurrent.TimeUnit; public final class IntUtils { private IntUtils() {} @@ -40,4 +41,18 @@ public static boolean between(int lower, int middle, int upper, boolean inclusiv default -> String.valueOf(count); }; } + + public static TimeUnit getTimeUnit(@NotNull String symbol) { + return switch (symbol) { + case "s" -> TimeUnit.SECONDS; + case "m" -> TimeUnit.MINUTES; + case "h" -> TimeUnit.HOURS; + case "d" -> TimeUnit.DAYS; + default -> null; + }; + } + + public static int convertToTicks(int length, TimeUnit unit) { + return Math.toIntExact(TimeUnit.SECONDS.convert(length, unit) * 20); + } } diff --git a/src/main/resources/TheElm.mixins.json b/src/main/resources/TheElm.mixins.json index cd22650..09743ee 100644 --- a/src/main/resources/TheElm.mixins.json +++ b/src/main/resources/TheElm.mixins.json @@ -17,6 +17,7 @@ "Commands.Stop", "Commands.Time", "Commands.Whisper", + "Commands.WorldBorder", "Blocks.BeeNest", "Blocks.FireSpread",