diff --git a/src/main/java/fr/openmc/api/datapacks/DatapackInjector.java b/src/main/java/fr/openmc/api/datapacks/DatapackInjector.java index c5fe96d7b..623ad9ead 100644 --- a/src/main/java/fr/openmc/api/datapacks/DatapackInjector.java +++ b/src/main/java/fr/openmc/api/datapacks/DatapackInjector.java @@ -2,11 +2,41 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import fr.openmc.api.datapacks.builders.ContentBuilder; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; public interface DatapackInjector { Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + String[] getPath(); + ContentBuilder getBuilder(); + String getNamespace(); + String getId(); + String getExtension(); - void inject(File rootFile); + default void inject(File rootFile) { + if (getBuilder() == null) return; + + Path root = rootFile.toPath().resolve("data").resolve(getNamespace()); + + for (String folder : getPath()) { + root = root.resolve(folder); + } + + try { + Files.createDirectories(root); + Path biomeFile = root.resolve(getId() + "." + getExtension()); + Files.createDirectories(biomeFile.getParent()); + Files.writeString(biomeFile, GSON.toJson(getBuilder().toJson())); + } catch (IOException e) { + throw new IllegalStateException("Cannot write Content files", e); + } + } + + default String getKey() { + return getNamespace() + ":" + getId(); + } } diff --git a/src/main/java/fr/openmc/api/datapacks/OMCDatapack.java b/src/main/java/fr/openmc/api/datapacks/OMCDatapack.java index f81f8a335..64e8b396e 100644 --- a/src/main/java/fr/openmc/api/datapacks/OMCDatapack.java +++ b/src/main/java/fr/openmc/api/datapacks/OMCDatapack.java @@ -18,15 +18,13 @@ @Getter @SuppressWarnings("UnstableApiUsage") public class OMCDatapack { - private final String packName; private final String namespace; private final Set injectors = new HashSet<>(); public final String ID_DATAPACK_INJECTED = "openmc-injected"; private final String ID_TEMP_DATAPACK_FOLDER = "datapacks-openmc"; - public OMCDatapack(String packName, String namespace) { - this.packName = packName; + public OMCDatapack(String namespace) { this.namespace = namespace; } diff --git a/src/main/java/fr/openmc/api/datapacks/builders/BiomeBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/BiomeBuilder.java index fa577c49f..9d7697eb0 100644 --- a/src/main/java/fr/openmc/api/datapacks/builders/BiomeBuilder.java +++ b/src/main/java/fr/openmc/api/datapacks/builders/BiomeBuilder.java @@ -28,7 +28,7 @@ * "temperature": 2 * } */ -public final class BiomeBuilder { +public final class BiomeBuilder implements ContentBuilder { private JsonObject attributes = new JsonObject(); private final JsonArray carvers = new JsonArray(); @Getter @@ -58,17 +58,17 @@ public BiomeBuilder features(JsonElement id) { } public BiomeBuilder temperatureModifier(String id) { - this.temperatureModifier =id; + this.temperatureModifier = id; return this; } public BiomeBuilder creatureSpawnProbability(Double value) { - this.creatureSpawnProbability=value; + this.creatureSpawnProbability = value; return this; } public BiomeBuilder downfall(Float value) { - this.downfall=value; + this.downfall = value; return this; } @@ -169,13 +169,16 @@ public JsonObject toJson() { if (attributes != null) json.add("attributes", attributes); if (temperatureModifier != null) json.addProperty("temperature_modifier", temperatureModifier); if (creatureSpawnProbability != null) json.addProperty("creature_spawn_probability", creatureSpawnProbability); - if (carvers != null) json.add("carvers", carvers); + json.add("carvers", carvers); if (downfall != null) json.addProperty("downfall", downfall); - if (effects != null) json.add("effects", effects); - if (features != null) json.add("features", features); + JsonObject effect = effects; + if (effect.get("water_color") == null) + effect.addProperty("water_color", "#000000"); + json.add("effects", effects); + json.add("features", features); if (hasPrecipitation != null) json.addProperty("has_precipitation", hasPrecipitation); - if (spawnCosts != null) json.add("spawn_costs", spawnCosts); - if (spawners != null) json.add("spawners", spawners); + json.add("spawn_costs", spawnCosts); + json.add("spawners", spawners); if (temperatures != null) json.addProperty("temperature", temperatures); return json; diff --git a/src/main/java/fr/openmc/api/datapacks/builders/ContentBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/ContentBuilder.java new file mode 100644 index 000000000..7c4a7797e --- /dev/null +++ b/src/main/java/fr/openmc/api/datapacks/builders/ContentBuilder.java @@ -0,0 +1,8 @@ +package fr.openmc.api.datapacks.builders; + +import com.google.gson.JsonObject; + +// * Interface qui permet au classes de reconnaitre que c'est un builder de contenu +public interface ContentBuilder { + JsonObject toJson(); +} diff --git a/src/main/java/fr/openmc/api/datapacks/builders/DimensionTypeBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/DimensionTypeBuilder.java index d09c5b354..2b3ddbd8c 100644 --- a/src/main/java/fr/openmc/api/datapacks/builders/DimensionTypeBuilder.java +++ b/src/main/java/fr/openmc/api/datapacks/builders/DimensionTypeBuilder.java @@ -32,7 +32,7 @@ * "timelines": "#minecraft:in_overworld" * } */ -public final class DimensionTypeBuilder { +public final class DimensionTypeBuilder implements ContentBuilder { private JsonObject attributes; private Double ambientLight = 0.0; private Double coordinateScale = 1.0; @@ -155,7 +155,7 @@ public DimensionTypeBuilder timelines(String timelines) { } public DimensionTypeBuilder timelines(TimelinesInjector injector) { - this.timelines = injector.getNamespace() + ":" + injector.getId(); + this.timelines = injector.getKey(); return this; } @@ -180,11 +180,4 @@ public JsonObject toJson() { if (timelines != null) json.addProperty("timelines", timelines); return json; } - - private JsonObject toOverridenEnvironnementAttribute(JsonElement value) { - JsonObject obj = new JsonObject(); - obj.addProperty("modifier", "override"); - obj.add("argument", value); - return obj; - } } diff --git a/src/main/java/fr/openmc/api/datapacks/builders/EnvironnementAttributeBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/EnvironnementAttributeBuilder.java index e32588f98..45dee16de 100644 --- a/src/main/java/fr/openmc/api/datapacks/builders/EnvironnementAttributeBuilder.java +++ b/src/main/java/fr/openmc/api/datapacks/builders/EnvironnementAttributeBuilder.java @@ -5,6 +5,7 @@ import fr.openmc.api.datapacks.builders.sounds.AmbientSoundBuilder; import org.bukkit.Particle; +import java.util.Map; import java.util.function.Consumer; public class EnvironnementAttributeBuilder { @@ -36,9 +37,25 @@ public EnvironnementAttributeBuilder attributes(JsonObject attributes) { * "minecraft:visual/ambient_particles": [ { "particle": { "type": "minecraft:crimson_spore" }, "probability": 0.025 } ] */ public EnvironnementAttributeBuilder ambientParticles(String particleType, double probability) { + return ambientParticles(particleType, probability, null); + } + + /** + * Ajoute un attribut "minecraft:visual/ambient_particles" simple. + * Exemple : + * "minecraft:visual/ambient_particles": [ { "particle": { "type": "minecraft:crimson_spore" }, "probability": 0.025 } ] + */ + public EnvironnementAttributeBuilder ambientParticles(String particleType, double probability, Map options) { JsonObject entry = new JsonObject(); JsonObject particle = new JsonObject(); particle.addProperty("type", particleType); + + if (options != null) { + for (var entry1 : options.entrySet()) { + particle.addProperty(entry1.getKey(), entry1.getValue()); + } + } + entry.add("particle", particle); entry.addProperty("probability", probability); @@ -94,6 +111,10 @@ public EnvironnementAttributeBuilder ambientParticles(Particle particle, double return ambientParticles(particle.getKey().toString(), probability); } + public EnvironnementAttributeBuilder ambientParticles(Particle particle, double probability, Map options) { + return ambientParticles(particle.getKey().toString(), probability, options); + } + public EnvironnementAttributeBuilder ambientSounds(AmbientSoundBuilder ambientBuilder) { this.attributes.add("audio/ambient_sounds", ambientBuilder.toJson()); return this; diff --git a/src/main/java/fr/openmc/api/datapacks/builders/TimelineBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/TimelineBuilder.java index bef824a5b..4869c3837 100644 --- a/src/main/java/fr/openmc/api/datapacks/builders/TimelineBuilder.java +++ b/src/main/java/fr/openmc/api/datapacks/builders/TimelineBuilder.java @@ -60,7 +60,7 @@ * } * } */ -public final class TimelineBuilder { +public final class TimelineBuilder implements ContentBuilder { private String clock = "minecraft:overworld"; private Integer periodTicks = null; private final Map tracks = new LinkedHashMap<>(); diff --git a/src/main/java/fr/openmc/api/datapacks/builders/dimensions/VoidDimensionBuilder.java b/src/main/java/fr/openmc/api/datapacks/builders/dimensions/VoidDimensionBuilder.java new file mode 100644 index 000000000..2a41fa693 --- /dev/null +++ b/src/main/java/fr/openmc/api/datapacks/builders/dimensions/VoidDimensionBuilder.java @@ -0,0 +1,58 @@ +package fr.openmc.api.datapacks.builders.dimensions; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import fr.openmc.api.datapacks.builders.ContentBuilder; +import fr.openmc.api.datapacks.injectors.BiomesInjector; +import fr.openmc.api.datapacks.injectors.DimensionTypesInjector; + +/** + * Exemple simple d'une dimension vide, le but n'est pas de faire une API + * pour build des datapacks sans toucher a un .json et ttes la structure qu'il y a derriere: + * { + * "type": "minecraft:overworld", + * "generator": { + * "type": "minecraft:flat", + * "settings": { + * "biome": "draft:draft", + * "lakes": false, + * "features": false, + * "layers": [] + * } + * } + * } + */ +public final class VoidDimensionBuilder implements ContentBuilder { + private String type = "minecraft:overworld"; + private String biome = "minecraft:plains"; + + public VoidDimensionBuilder type(DimensionTypesInjector injector) { + this.type = injector.getKey(); + return this; + } + + public VoidDimensionBuilder biome(BiomesInjector injector) { + this.biome = injector.getKey(); + return this; + } + + public JsonObject toJson() { + JsonObject json = new JsonObject(); + if (type != null) json.addProperty("type", type); + if (biome != null) { + JsonObject generator = new JsonObject(); + generator.addProperty("type", "minecraft:flat"); + + JsonObject settings = new JsonObject(); + settings.addProperty("biome", biome); + settings.addProperty("lakes", false); + settings.addProperty("features", false); + settings.add("layers", new JsonArray()); + + generator.add("settings", settings); + json.add("generator", generator); + } + + return json; + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/api/datapacks/injectors/BiomesInjector.java b/src/main/java/fr/openmc/api/datapacks/injectors/BiomesInjector.java index c1db7f905..290b84741 100644 --- a/src/main/java/fr/openmc/api/datapacks/injectors/BiomesInjector.java +++ b/src/main/java/fr/openmc/api/datapacks/injectors/BiomesInjector.java @@ -2,13 +2,8 @@ import fr.openmc.api.datapacks.DatapackInjector; import fr.openmc.api.datapacks.builders.BiomeBuilder; +import fr.openmc.api.datapacks.builders.ContentBuilder; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.function.Consumer; /** @@ -19,39 +14,44 @@ public class BiomesInjector implements DatapackInjector { private final String namespace; - private final Map entries = new LinkedHashMap<>(); + private final String id; + private final BiomeBuilder builder; - public BiomesInjector(String namespace) { + public BiomesInjector(String namespace, String id, BiomeBuilder builder) { this.namespace = namespace; + this.id = id; + this.builder = builder; } - public BiomesInjector add(String id, Consumer builder) { - BiomeBuilder instance = new BiomeBuilder(); - builder.accept(instance); - entries.put(id, instance); - return this; + public BiomesInjector(String namespace, String id, Consumer builder) { + this.namespace = namespace; + this.id = id; + this.builder = new BiomeBuilder(); + builder.accept(this.builder); + } + + @Override + public String[] getPath() { + return new String[]{"worldgen", "biome"}; } - public BiomesInjector add(String id, BiomeBuilder builder) { - entries.put(id, builder); - return this; + @Override + public ContentBuilder getBuilder() { + return builder; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getId() { + return id; } @Override - public void inject(File rootFile) { - if (entries.isEmpty()) return; - - Path root = rootFile.toPath().resolve("data").resolve(namespace) - .resolve("worldgen").resolve("biome"); - try { - Files.createDirectories(root); - for (var entry : entries.entrySet()) { - Path biomeFile = root.resolve(entry.getKey() + ".json"); - Files.createDirectories(biomeFile.getParent()); - Files.writeString(biomeFile, GSON.toJson(entry.getValue().toJson())); - } - } catch (IOException e) { - throw new IllegalStateException("Cannot write biome files", e); - } + public String getExtension() { + return "json"; } } diff --git a/src/main/java/fr/openmc/api/datapacks/injectors/DimensionInjector.java b/src/main/java/fr/openmc/api/datapacks/injectors/DimensionInjector.java new file mode 100644 index 000000000..346eb89f4 --- /dev/null +++ b/src/main/java/fr/openmc/api/datapacks/injectors/DimensionInjector.java @@ -0,0 +1,42 @@ +package fr.openmc.api.datapacks.injectors; + +import fr.openmc.api.datapacks.DatapackInjector; +import fr.openmc.api.datapacks.builders.ContentBuilder; + +public class DimensionInjector implements DatapackInjector { + + private final String namespace; + private final String id; + private final ContentBuilder builder; + + public DimensionInjector(String namespace, String id, ContentBuilder builder) { + this.namespace = namespace; + this.id = id; + this.builder = builder; + } + + @Override + public String[] getPath() { + return new String[]{"dimension"}; + } + + @Override + public ContentBuilder getBuilder() { + return builder; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getExtension() { + return "json"; + } +} diff --git a/src/main/java/fr/openmc/api/datapacks/injectors/DimensionTypesInjector.java b/src/main/java/fr/openmc/api/datapacks/injectors/DimensionTypesInjector.java index 7a3ba0a8d..3c3098f2b 100644 --- a/src/main/java/fr/openmc/api/datapacks/injectors/DimensionTypesInjector.java +++ b/src/main/java/fr/openmc/api/datapacks/injectors/DimensionTypesInjector.java @@ -1,14 +1,9 @@ package fr.openmc.api.datapacks.injectors; import fr.openmc.api.datapacks.DatapackInjector; +import fr.openmc.api.datapacks.builders.ContentBuilder; import fr.openmc.api.datapacks.builders.DimensionTypeBuilder; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.function.Consumer; /** @@ -18,38 +13,44 @@ public class DimensionTypesInjector implements DatapackInjector { private final String namespace; - private final Map entries = new LinkedHashMap<>(); + private final String id; + private final DimensionTypeBuilder builder; - public DimensionTypesInjector(String namespace) { + public DimensionTypesInjector(String namespace, String id, DimensionTypeBuilder builder) { this.namespace = namespace; + this.id = id; + this.builder = builder; } - public DimensionTypesInjector add(String id, Consumer builder) { - DimensionTypeBuilder instance = new DimensionTypeBuilder(); - builder.accept(instance); - entries.put(id, instance); - return this; + public DimensionTypesInjector(String namespace, String id, Consumer builder) { + this.namespace = namespace; + this.id = id; + this.builder = new DimensionTypeBuilder(); + builder.accept(this.builder); + } + + @Override + public String[] getPath() { + return new String[]{"dimension_type"}; } - public DimensionTypesInjector add(String id, DimensionTypeBuilder builder) { - entries.put(id, builder); - return this; + @Override + public ContentBuilder getBuilder() { + return builder; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getId() { + return id; } @Override - public void inject(File rootFile) { - if (entries.isEmpty()) return; - - Path root = rootFile.toPath().resolve("data").resolve(namespace).resolve("dimension_type"); - try { - Files.createDirectories(root); - for (var entry : entries.entrySet()) { - Path dimensionTypeFile = root.resolve(entry.getKey() + ".json"); - Files.createDirectories(dimensionTypeFile.getParent()); - Files.writeString(dimensionTypeFile, GSON.toJson(entry.getValue().toJson())); - } - } catch (IOException e) { - throw new IllegalStateException("Cannot write dimension_type files", e); - } + public String getExtension() { + return "json"; } } diff --git a/src/main/java/fr/openmc/api/datapacks/injectors/PackMetadataInjector.java b/src/main/java/fr/openmc/api/datapacks/injectors/PackMetadataInjector.java index dc6681ffc..3b81eb78b 100644 --- a/src/main/java/fr/openmc/api/datapacks/injectors/PackMetadataInjector.java +++ b/src/main/java/fr/openmc/api/datapacks/injectors/PackMetadataInjector.java @@ -1,6 +1,7 @@ package fr.openmc.api.datapacks.injectors; import fr.openmc.api.datapacks.DatapackInjector; +import fr.openmc.api.datapacks.builders.ContentBuilder; import java.io.File; import java.io.IOException; @@ -33,4 +34,29 @@ private String packMcMeta() { } """, PACK_FORMAT[0], PACK_FORMAT[0], PACK_FORMAT[1], PACK_FORMAT[0], PACK_FORMAT[1]); } + + @Override + public String[] getPath() { + return null; + } + + @Override + public ContentBuilder getBuilder() { + return null; + } + + @Override + public String getNamespace() { + return null; + } + + @Override + public String getId() { + return null; + } + + @Override + public String getExtension() { + return null; + } } diff --git a/src/main/java/fr/openmc/api/datapacks/injectors/TimelinesInjector.java b/src/main/java/fr/openmc/api/datapacks/injectors/TimelinesInjector.java index 408d3bdf1..90b802e5e 100644 --- a/src/main/java/fr/openmc/api/datapacks/injectors/TimelinesInjector.java +++ b/src/main/java/fr/openmc/api/datapacks/injectors/TimelinesInjector.java @@ -1,15 +1,10 @@ package fr.openmc.api.datapacks.injectors; import fr.openmc.api.datapacks.DatapackInjector; +import fr.openmc.api.datapacks.builders.ContentBuilder; import fr.openmc.api.datapacks.builders.TimelineBuilder; import lombok.Getter; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.function.Consumer; @Getter @@ -21,41 +16,44 @@ public class TimelinesInjector implements DatapackInjector { private final String namespace; - private String id; - private final Map entries = new LinkedHashMap<>(); + private final String id; + private final TimelineBuilder builder; - public TimelinesInjector(String namespace) { + public TimelinesInjector(String namespace, String id, TimelineBuilder builder) { this.namespace = namespace; + this.id = id; + this.builder = builder; } - public TimelinesInjector add(String id, Consumer builder) { - TimelineBuilder instance = new TimelineBuilder(); - builder.accept(instance); - entries.put(id, instance); + public TimelinesInjector(String namespace, String id, Consumer builder) { + this.namespace = namespace; this.id = id; - return this; + this.builder = new TimelineBuilder(); + builder.accept(this.builder); } - public TimelinesInjector add(String id, TimelineBuilder builder) { - entries.put(id, builder); - this.id = id; - return this; + @Override + public String[] getPath() { + return new String[]{"timeline"}; + } + + @Override + public ContentBuilder getBuilder() { + return builder; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getId() { + return id; } @Override - public void inject(File rootFile) { - if (entries.isEmpty()) return; - - Path root = rootFile.toPath().resolve("data").resolve(namespace).resolve("timeline"); - try { - Files.createDirectories(root); - for (var entry : entries.entrySet()) { - Path file = root.resolve(entry.getKey() + ".json"); - Files.createDirectories(file.getParent()); - Files.writeString(file, GSON.toJson(entry.getValue().toJson())); - } - } catch (IOException e) { - throw new IllegalStateException("Cannot write timeline files", e); - } + public String getExtension() { + return "json"; } } diff --git a/src/main/java/fr/openmc/core/OMCRegistry.java b/src/main/java/fr/openmc/core/OMCRegistry.java index 4d271ea61..18e54c08a 100644 --- a/src/main/java/fr/openmc/core/OMCRegistry.java +++ b/src/main/java/fr/openmc/core/OMCRegistry.java @@ -13,6 +13,7 @@ import fr.openmc.core.registry.lootboxes.CustomLootboxRegistry; import fr.openmc.core.registry.loottable.CustomLootTableRegistry; import fr.openmc.core.registry.mobs.CustomMobRegistry; +import fr.openmc.core.registry.worldtemplates.WorldTemplateRegistry; import io.papermc.paper.plugin.bootstrap.BootstrapContext; import java.io.IOException; @@ -34,6 +35,8 @@ public final class OMCRegistry { public static WeeklyEventsRegistry WEEKLY_EVENTS; public static DailyEventsRegistry DAILY_EVENTS; + public static WorldTemplateRegistry WORLD_TEMPLATES; + private static final List LOADED = new ArrayList<>(); private static final List ALL = List.of( @@ -54,6 +57,9 @@ public final class OMCRegistry { RegistryLoadingType.AFTER_IA), new RegistryContext(() -> CUSTOM_MOBS = new CustomMobRegistry(), RegistryLoadingType.AFTER_IA), + + new RegistryContext(() -> WORLD_TEMPLATES = new WorldTemplateRegistry(), + RegistryLoadingType.BOOTSTRAP, RegistryLoadingType.RUNTIME), new RegistryContext(() -> WEEKLY_EVENTS = new WeeklyEventsRegistry(), RegistryLoadingType.AFTER_IA), new RegistryContext(() -> DAILY_EVENTS = new DailyEventsRegistry(), diff --git a/src/main/java/fr/openmc/core/features/singularity/contents/worldtemplates/SingularityWorldTemplate.java b/src/main/java/fr/openmc/core/features/singularity/contents/worldtemplates/SingularityWorldTemplate.java new file mode 100644 index 000000000..533188a66 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/singularity/contents/worldtemplates/SingularityWorldTemplate.java @@ -0,0 +1,105 @@ +package fr.openmc.core.features.singularity.contents.worldtemplates; + +import fr.openmc.api.datapacks.builders.BiomeBuilder; +import fr.openmc.api.datapacks.builders.DimensionTypeBuilder; +import fr.openmc.api.datapacks.builders.EnvironnementAttributeBuilder; +import fr.openmc.core.bootstrap.features.Feature; +import fr.openmc.core.bootstrap.features.types.HasFeature; +import fr.openmc.core.features.singularity.sub.worldsfx.SingularityWorldManager; +import fr.openmc.core.registry.worldtemplates.WorldTemplate; +import fr.openmc.core.registry.worldtemplates.interfaces.HasGamerules; +import fr.openmc.core.registry.worldtemplates.interfaces.HasWorldBorder; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.GameRule; +import org.bukkit.GameRules; +import org.bukkit.Particle; + +import java.util.HashMap; +import java.util.Map; + +public class SingularityWorldTemplate extends WorldTemplate + implements HasWorldBorder, HasGamerules, HasFeature { + @Override + public String getNamespace() { + return "omc_singularity"; + } + + @Override + public String getId() { + return "singularity_world"; + } + + @Override + public DimensionTypeBuilder dimensionType() { + return new DimensionTypeBuilder() + .attributesBuilder(new EnvironnementAttributeBuilder() + .attributes(obj -> { + obj.addProperty("visual/ambient_light_color", "#C4C4C4"); + obj.addProperty("visual/block_light_tint", "#1AFFE4"); + obj.addProperty("visual/night_vision_color", "#61F5FF"); + + obj.addProperty("visual/fog_start_distance", 64); + obj.addProperty("visual/fog_end_distance", 174); + + obj.addProperty("minecraft:visual/sky_light_color", "#f7f7f7"); + obj.addProperty("visual/fog_color","#E8E8E8"); + }) + .ambientParticles(Particle.ENCHANT, 0.02f) + .ambientParticles(Particle.FLASH, 0.0007f, Map.of("color", 14342874))) + .defaultClock(null) + .ambientLight(0f) + .cardinalLight("nether") + .timelines("#minecraft:in_nether") + .skybox(DimensionType.Skybox.NONE) + .hasSkylight(true) + .hasCeiling(true) + .hasFixedTime(true); + } + + @Override + public BiomeBuilder biome() { + return new BiomeBuilder() + .grassColor("#ADADAD"); + } + + @Override + public Map, Object> getGamerules() { + Map, Object> gamerules = new HashMap<>(); + + gamerules.put(GameRules.ADVANCE_TIME, Boolean.FALSE); + gamerules.put(GameRules.ADVANCE_WEATHER, Boolean.FALSE); + gamerules.put(GameRules.LOCATOR_BAR, Boolean.FALSE); + gamerules.put(GameRules.PVP, Boolean.FALSE); + gamerules.put(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS, Boolean.FALSE); + + gamerules.put(GameRules.TNT_EXPLODES, Boolean.FALSE); + + gamerules.put(GameRules.SPAWN_MOBS, Boolean.FALSE); + gamerules.put(GameRules.SPAWN_MONSTERS, Boolean.FALSE); + gamerules.put(GameRules.SPAWN_PATROLS, Boolean.FALSE); + gamerules.put(GameRules.SPAWN_PHANTOMS, Boolean.FALSE); + gamerules.put(GameRules.SPAWN_WARDENS, Boolean.FALSE); + gamerules.put(GameRules.SPAWN_WANDERING_TRADERS, Boolean.FALSE); + gamerules.put(GameRules.SPAWNER_BLOCKS_WORK, Boolean.FALSE); + + return gamerules; + } + + @Override + public double[] getCenter() { + double[] center = new double[2]; + center[0] = 0; + center[1] = 0; + return center; + } + + @Override + public double getSize() { + return 5000; + } + + @Override + public Feature getFeature() { + return new SingularityWorldManager(this); + } +} diff --git a/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/SingularityWorldManager.java b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/SingularityWorldManager.java new file mode 100644 index 000000000..b3ff6cccd --- /dev/null +++ b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/SingularityWorldManager.java @@ -0,0 +1,51 @@ +package fr.openmc.core.features.singularity.sub.worldsfx; + +import fr.openmc.core.bootstrap.features.Feature; +import fr.openmc.core.features.singularity.sub.worldsfx.sfx.ImpulsionSingularitySFX; +import fr.openmc.core.features.singularity.sub.worldsfx.sfx.InstabilitySingularitySFX; +import fr.openmc.core.features.singularity.sub.worldsfx.sfx.PulseSingularitySFX; +import fr.openmc.core.registry.worldtemplates.WorldTemplate; +import lombok.Getter; +import org.bukkit.Location; + +/** + * Classe gérant les SFX (Effets Spéciaux) de la Dimension inclus dedans : + * - les Impulsions de la Singularité + * - la gravité des joueurs + * - les intéractions avec la Singularité + */ +public class SingularityWorldManager extends Feature { + + public static Location origin; + public static WorldTemplate worldTemplate; + + @Getter + private PulseSingularitySFX pulseSingularitySFX; + @Getter + private ImpulsionSingularitySFX impulsionSingularitySFX; + @Getter + private InstabilitySingularitySFX instabilitySingularitySFX; + + public SingularityWorldManager(WorldTemplate template) { + worldTemplate = template; + origin = new Location(template.getWorld(), 0, 100, 0); + } + + @Override + public void init() { + pulseSingularitySFX = new PulseSingularitySFX(origin); + impulsionSingularitySFX = new ImpulsionSingularitySFX(origin); + instabilitySingularitySFX = new InstabilitySingularitySFX(origin); + + pulseSingularitySFX.start(); + impulsionSingularitySFX.start(); + instabilitySingularitySFX.start(); + } + + @Override + public void save() { + pulseSingularitySFX.stop(); + impulsionSingularitySFX.stop(); + instabilitySingularitySFX.stop(); + } +} diff --git a/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/ImpulsionSingularitySFX.java b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/ImpulsionSingularitySFX.java new file mode 100644 index 000000000..a678d98f4 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/ImpulsionSingularitySFX.java @@ -0,0 +1,46 @@ +package fr.openmc.core.features.singularity.sub.worldsfx.sfx; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.utils.bukkit.ParticleUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.scheduler.BukkitTask; + +import java.time.LocalDateTime; + +public class ImpulsionSingularitySFX { + public static final long IMPULSION_INTERVAL = 10L; // 10 minutes + public static LocalDateTime lastImpulsion = LocalDateTime.now(); + + private BukkitTask currentTask; + private final Location origin; + + public ImpulsionSingularitySFX(Location origin) { + this.origin = origin; + } + + public void start() { + currentTask = Bukkit.getScheduler().runTaskTimer(OMCPlugin.getInstance(), () -> { + lastImpulsion = LocalDateTime.now(); + + if (!origin.getWorld().getPlayers().isEmpty()) { + origin.getWorld().playSound(origin, Sound.ENTITY_WARDEN_SONIC_BOOM, 156.0f, 0.1f); + origin.getWorld().playSound(origin, Sound.BLOCK_BEACON_POWER_SELECT, 156.0f, 0.1f); + ParticleUtils.spawnRepulsedParticlesSpherical(origin, Particle.SNEEZE, 500, 400, 400, 20 * 50, null); + } + }, 0L, IMPULSION_INTERVAL * 60 * 20); + } + + public void stop() { + if (currentTask != null) { + currentTask.cancel(); + currentTask = null; + } + } + + public static LocalDateTime getNextImpulsion() { + return lastImpulsion.plusMinutes(IMPULSION_INTERVAL); + } +} diff --git a/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/InstabilitySingularitySFX.java b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/InstabilitySingularitySFX.java new file mode 100644 index 000000000..615458c88 --- /dev/null +++ b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/InstabilitySingularitySFX.java @@ -0,0 +1,45 @@ +package fr.openmc.core.features.singularity.sub.worldsfx.sfx; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.utils.RandomUtils; +import fr.openmc.core.utils.bukkit.ParticleUtils; +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.scheduler.BukkitTask; + +public class InstabilitySingularitySFX { + private BukkitTask currentTask; + private final Location origin; + + private final int MIN_INTERVAL = 1; // en secondes + private final int MAX_INTERVAL = 4; // en secondes + + public InstabilitySingularitySFX(Location origin) { + this.origin = origin; + } + + public void start() { + scheduleNextPulse(0); + } + + public void stop() { + if (currentTask != null) { + currentTask.cancel(); + currentTask = null; + } + } + + private void scheduleNextPulse(long delay) { + currentTask = Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), () -> { + if (!origin.getWorld().getPlayers().isEmpty()) + ParticleUtils.spawnParticlesInCube( + origin, Particle.FLASH, 40, 20, + Color.fromRGB(92, 250, 235)); + + long nextDelay = RandomUtils.randomBetween(MIN_INTERVAL, MAX_INTERVAL) * 20L; + scheduleNextPulse(nextDelay); + }, delay); + } +} diff --git a/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/PulseSingularitySFX.java b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/PulseSingularitySFX.java new file mode 100644 index 000000000..18cc47abf --- /dev/null +++ b/src/main/java/fr/openmc/core/features/singularity/sub/worldsfx/sfx/PulseSingularitySFX.java @@ -0,0 +1,64 @@ +package fr.openmc.core.features.singularity.sub.worldsfx.sfx; + +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.utils.bukkit.ParticleUtils; +import org.bukkit.*; +import org.bukkit.scheduler.BukkitTask; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +public class PulseSingularitySFX { + + private BukkitTask currentTask; + private final Location origin; + + private boolean isConverging = true; + private final long convergeInterval = 20 * 3L; // 3 sec + + public PulseSingularitySFX(Location origin) { + this.origin = origin; + } + + public void start() { + scheduleNextPulse(0); + } + + public void stop() { + if (currentTask != null) { + currentTask.cancel(); + currentTask = null; + } + } + + private void scheduleNextPulse(long delay) { + currentTask = Bukkit.getScheduler().runTaskLater(OMCPlugin.getInstance(), this::pulse, delay); + } + + private void pulse() { + long nextInverval = getNextInterval(); + if (!origin.getWorld().getPlayers().isEmpty()) + if (isConverging) { + origin.getWorld().playSound(origin, Sound.ENTITY_WARDEN_SONIC_CHARGE, 10.0f, 0.6f); + ParticleUtils.spawnConvergingParticlesSpherical(origin, Particle.GLOW_SQUID_INK, 250, 100.0, 150, (int) nextInverval + 40, null); + } else { + origin.getWorld().playSound(origin, Sound.ENTITY_WARDEN_SONIC_BOOM, 11.0f, 0.5f); + ParticleUtils.spawnRepulsedParticlesSpherical(origin, Particle.FLASH, 400, 120, 150,(int) nextInverval + 40, Color.fromRGB(93, 217, 210)); + } + + isConverging = !isConverging; + + scheduleNextPulse(nextInverval); + } + + private long getNextInterval() { + LocalDateTime now = LocalDateTime.now(); + + double nextImpulsion = now.until(ImpulsionSingularitySFX.getNextImpulsion(), ChronoUnit.SECONDS); + double impulsionInterval = ImpulsionSingularitySFX.IMPULSION_INTERVAL * 60; + + double ratio = Math.clamp(nextImpulsion / impulsionInterval, 0.5, 1.0); + + return (long) (convergeInterval * ratio); + } +} diff --git a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbient.java b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbient.java index 589eb9a17..adb1cff94 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbient.java +++ b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbient.java @@ -6,10 +6,10 @@ import fr.openmc.core.features.leaderboards.LeaderboardManager; import fr.openmc.core.registry.ambient.builder.AmbientBuilder; import fr.openmc.core.utils.MathUtils; -import fr.openmc.core.utils.nms.PlayerBiomeNMS; -import fr.openmc.core.utils.nms.PlayerRespawnNMS; -import fr.openmc.core.utils.nms.PlayerSetTimeNMS; -import fr.openmc.core.utils.nms.PlayerWeatherNMS; +import fr.openmc.core.utils.nms.player.PlayerBiomeNMS; +import fr.openmc.core.utils.nms.player.PlayerRespawnNMS; +import fr.openmc.core.utils.nms.player.PlayerSetTimeNMS; +import fr.openmc.core.utils.nms.player.PlayerWeatherNMS; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; @@ -178,7 +178,7 @@ public BiomesInjector toBiomeVariant(Biome initialBiome, Identifier ambientId) { foliageColor.ifPresent(builder::foliageColor); dryFoliageColor.ifPresent(builder::dryFoliageColor); - return new BiomesInjector(ambientId.getNamespace()).add(ambientId.getPath(), builder); + return new BiomesInjector(ambientId.getNamespace(), ambientId.getPath(), builder); } private boolean hasEffects(JsonObject effects, String envKey) { diff --git a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java index 48b026fbc..a1f88f40b 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java +++ b/src/main/java/fr/openmc/core/registry/ambient/CustomAmbientRegistry.java @@ -29,7 +29,7 @@ public class CustomAmbientRegistry extends Registry implements KeyedRegistry, HasListeners { public static final String NAMESPACE = "omc_ambient"; - private final OMCDatapack ambientDatapack = new OMCDatapack("openmc", NAMESPACE); + private final OMCDatapack ambientDatapack = new OMCDatapack(NAMESPACE); // ** REGISTER AMBIENT ** public final CustomAmbient DARK = register(new DarkAmbient()); diff --git a/src/main/java/fr/openmc/core/registry/ambient/builder/AmbientBuilder.java b/src/main/java/fr/openmc/core/registry/ambient/builder/AmbientBuilder.java index 470f067b9..e8d0fb3ed 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/builder/AmbientBuilder.java +++ b/src/main/java/fr/openmc/core/registry/ambient/builder/AmbientBuilder.java @@ -101,7 +101,7 @@ public AmbientBuilder timelines(String timelines) { } public AmbientBuilder timelines(TimelineBuilder builder) { - this.dimTypeBuilder.timelines(new TimelinesInjector(namespace).add(id, builder)); + this.dimTypeBuilder.timelines(new TimelinesInjector(namespace, id, builder)); this.timelineBuilder = builder; return this; } @@ -209,12 +209,12 @@ public AmbientBuilder grassColorModifier(String id) { public void runInjectors(CustomAmbient ambient, OMCDatapack datapack) { // ** DimensionType Injector - DimensionTypesInjector dimensionTypesInjector = new DimensionTypesInjector(namespace).add(id, dimTypeBuilder); + DimensionTypesInjector dimensionTypesInjector = new DimensionTypesInjector(namespace, id, dimTypeBuilder); datapack.addInjector(dimensionTypesInjector); // ** Timeline Injector if (timelineBuilder != null) { - TimelinesInjector timelinesInjector = new TimelinesInjector(namespace).add(id, timelineBuilder); + TimelinesInjector timelinesInjector = new TimelinesInjector(namespace, id, timelineBuilder); datapack.addInjector(timelinesInjector); } diff --git a/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientFixedTimeListener.java b/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientFixedTimeListener.java index 144e81755..7cc07e5f1 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientFixedTimeListener.java +++ b/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientFixedTimeListener.java @@ -6,7 +6,7 @@ import fr.openmc.core.events.RegionEnterEvent; import fr.openmc.core.events.RegionLeaveEvent; import fr.openmc.core.registry.ambient.CustomAmbient; -import fr.openmc.core.utils.nms.PlayerSetTimeNMS; +import fr.openmc.core.utils.nms.player.PlayerSetTimeNMS; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientWeatherListener.java b/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientWeatherListener.java index 2cf6e1467..1ef3a2963 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientWeatherListener.java +++ b/src/main/java/fr/openmc/core/registry/ambient/listeners/AmbientWeatherListener.java @@ -6,7 +6,7 @@ import fr.openmc.core.events.RegionEnterEvent; import fr.openmc.core.events.RegionLeaveEvent; import fr.openmc.core.registry.ambient.CustomAmbient; -import fr.openmc.core.utils.nms.PlayerWeatherNMS; +import fr.openmc.core.utils.nms.player.PlayerWeatherNMS; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/src/main/java/fr/openmc/core/registry/ambient/listeners/BiomesOnChunkLoad.java b/src/main/java/fr/openmc/core/registry/ambient/listeners/BiomesOnChunkLoad.java index bdd0fef43..2d5bff2c0 100644 --- a/src/main/java/fr/openmc/core/registry/ambient/listeners/BiomesOnChunkLoad.java +++ b/src/main/java/fr/openmc/core/registry/ambient/listeners/BiomesOnChunkLoad.java @@ -3,7 +3,7 @@ import fr.openmc.core.OMCRegistry; import fr.openmc.core.bootstrap.features.types.NotLoadInUnitTest; import fr.openmc.core.registry.ambient.CustomAmbient; -import fr.openmc.core.utils.nms.PlayerBiomeNMS; +import fr.openmc.core.utils.nms.player.PlayerBiomeNMS; import io.papermc.paper.event.packet.PlayerChunkLoadEvent; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.chunk.ChunkAccess; diff --git a/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplate.java b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplate.java new file mode 100644 index 000000000..485634cf1 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplate.java @@ -0,0 +1,77 @@ +package fr.openmc.core.registry.worldtemplates; + +import fr.openmc.api.datapacks.builders.BiomeBuilder; +import fr.openmc.api.datapacks.builders.DimensionTypeBuilder; +import fr.openmc.core.registry.worldtemplates.interfaces.HasGamerules; +import fr.openmc.core.registry.worldtemplates.interfaces.HasWorldBorder; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import org.bukkit.*; +import org.bukkit.block.Biome; + +import java.io.File; +import java.nio.file.Path; +import java.util.Map; + +@SuppressWarnings("UnstableApiUsage") +public abstract class WorldTemplate { + private static Registry BIOME_REGISTRY = null; + private World world = null; + + // * a @Override + public void onFirstLoad() {} + + public void firstLoad() { + World world = getWorld(); + + // * impl HasGamerules + if (this instanceof HasGamerules gamerules) { + Map, Object> gamerulesMap = gamerules.getGamerules(); + for (Map.Entry, Object> entry : gamerulesMap.entrySet()) { + HasGamerules.applyRule(world, entry.getKey(), entry.getValue()); + } + } + + // * impl hasWorldBorder + if (this instanceof HasWorldBorder worldBorder) { + WorldBorder worldBorder1 = world.getWorldBorder(); + + worldBorder1.setCenter(worldBorder.getCenter()[0], worldBorder.getCenter()[1]); + worldBorder1.setSize(worldBorder.getSize()); + } + + onFirstLoad(); + } + + public abstract String getNamespace(); + public abstract String getId(); + public abstract DimensionTypeBuilder dimensionType(); + public abstract BiomeBuilder biome(); + + public Biome getBiome() { + if (BIOME_REGISTRY == null) + BIOME_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.BIOME); + return BIOME_REGISTRY.getOrThrow(getKey()); + } + + public World getWorld() { + if (world == null) + world = Bukkit.getWorld(getKey()); + return world; + } + + public NamespacedKey getKey() { + return NamespacedKey.fromString(getNamespace() + ":" + getId()); + } + + public boolean isAlreadyCreated(Path dataPath) { + File pluginsDir = dataPath.toFile().getParentFile().getParentFile(); // * root + File worldDir = new File(pluginsDir, "world"); // * root/world + File dimensionsDir = new File(worldDir, "dimensions"); // * root/world/dimensions + File namespaceDir = new File(dimensionsDir, getNamespace()); // * root/world/dimensions// + File idDir = new File(namespaceDir, getId()); // * root/world/dimensions// + File dataDir = new File(idDir, "data"); // * root/world/dimensions///data + + return dataDir.exists() && dataDir.isDirectory(); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateConfig.java b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateConfig.java new file mode 100644 index 000000000..8fa2dd0cd --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateConfig.java @@ -0,0 +1,52 @@ +package fr.openmc.core.registry.worldtemplates; + +import fr.openmc.core.bootstrap.integration.OMCLogger; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class WorldTemplateConfig { + private static File worldTemplateFile; + private static FileConfiguration worldTemplateConfig; + + public static void init(File dataFolder) { + worldTemplateFile = new File(dataFolder + "/data/registry", "world_template.yml"); + worldTemplateConfig = YamlConfiguration.loadConfiguration(worldTemplateFile); + + // * Premier lancement du plugin où suppression du fichier par une classe externe (ex CustomAmbientRegistry) + if (!worldTemplateFile.exists()) { + worldTemplateConfig.set("first_loaded", new ArrayList<>()); + saveConfig(); + } + } + + public static boolean hasFirstLoaded(WorldTemplate template) { + return worldTemplateConfig.getStringList("first_loaded").contains(template.getKey().asString()); + } + + public static void addFirstLoaded(WorldTemplate template) { + List biomesLoaded = worldTemplateConfig.getStringList("first_loaded"); + biomesLoaded.add(template.getKey().asString()); + worldTemplateConfig.set("first_loaded", biomesLoaded); + saveConfig(); + } + + public static void removeFirstLoaded(WorldTemplate template) { + List biomesLoaded = worldTemplateConfig.getStringList("first_loaded"); + biomesLoaded.remove(template.getKey().asString()); + worldTemplateConfig.set("first_loaded", biomesLoaded); + saveConfig(); + } + + private static void saveConfig() { + try { + worldTemplateConfig.save(worldTemplateFile); + } catch (IOException e) { + OMCLogger.error("Cannot save worldTemplateConfigFile", e); + } + } +} diff --git a/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateRegistry.java b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateRegistry.java new file mode 100644 index 000000000..a69457980 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/worldtemplates/WorldTemplateRegistry.java @@ -0,0 +1,75 @@ +package fr.openmc.core.registry.worldtemplates; + +import fr.openmc.api.datapacks.OMCDatapack; +import fr.openmc.api.datapacks.builders.dimensions.VoidDimensionBuilder; +import fr.openmc.api.datapacks.injectors.BiomesInjector; +import fr.openmc.api.datapacks.injectors.DimensionInjector; +import fr.openmc.api.datapacks.injectors.DimensionTypesInjector; +import fr.openmc.core.OMCPlugin; +import fr.openmc.core.bootstrap.features.types.HasFeature; +import fr.openmc.core.bootstrap.registries.KeyedRegistry; +import fr.openmc.core.bootstrap.registries.Registry; +import fr.openmc.core.features.singularity.contents.worldtemplates.SingularityWorldTemplate; +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import org.bukkit.World; + +import java.io.IOException; +import java.util.Optional; + +@SuppressWarnings("UnstableApiUsage") +public class WorldTemplateRegistry extends Registry + implements KeyedRegistry { + + // ** REGISTER WORLD TEMPLATES ** + public final WorldTemplate SINGULARITY_WORLD = register(new SingularityWorldTemplate()); + + @Override + public void bootstrap(BootstrapContext context) throws IOException { + WorldTemplateConfig.init(context.getDataDirectory().toFile()); + + // * Initialise le dimension type et le biome associé à la map + for (WorldTemplate template : values()) { + if (!template.isAlreadyCreated(context.getDataDirectory())) + WorldTemplateConfig.removeFirstLoaded(template); + + OMCDatapack worldTemplateDatapack = new OMCDatapack(template.getNamespace()); + + DimensionTypesInjector dimTypeInjector = new DimensionTypesInjector(template.getNamespace(), template.getId(), template.dimensionType()); + worldTemplateDatapack.addInjector(dimTypeInjector); + BiomesInjector biomeInjector = new BiomesInjector(template.getNamespace(), template.getId(), template.biome()); + worldTemplateDatapack.addInjector(biomeInjector); + worldTemplateDatapack.addInjector(new DimensionInjector( + template.getNamespace(), + template.getId(), + new VoidDimensionBuilder() + .biome(biomeInjector) + .type(dimTypeInjector))); + + worldTemplateDatapack.buildBootstrap(context, true); // todo: remettre sur false qd fini de debug + } + } + + @Override + public void init() { + for (WorldTemplate template : values()) { + if (template instanceof HasFeature hasFeature) + OMCPlugin.registerFeature(hasFeature.getFeature()); + + if (WorldTemplateConfig.hasFirstLoaded(template)) continue; + template.firstLoad(); + WorldTemplateConfig.addFirstLoaded(template); + } + } + + @Override + public String key(WorldTemplate registryObject) { + return registryObject.getKey().asString(); + } + + public WorldTemplate getByWorld(World world) { + Optional template = get(world.getKey().asString()); + + if (template.isEmpty()) return null; + return template.get(); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasGamerules.java b/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasGamerules.java new file mode 100644 index 000000000..82fb85355 --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasGamerules.java @@ -0,0 +1,15 @@ +package fr.openmc.core.registry.worldtemplates.interfaces; + +import org.bukkit.GameRule; +import org.bukkit.World; + +import java.util.Map; + +public interface HasGamerules { + Map, Object> getGamerules(); + + @SuppressWarnings("unchecked") + public static void applyRule(World world, GameRule rule, Object value) { + world.setGameRule(rule, (T) value); + } +} diff --git a/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasWorldBorder.java b/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasWorldBorder.java new file mode 100644 index 000000000..f249ec50e --- /dev/null +++ b/src/main/java/fr/openmc/core/registry/worldtemplates/interfaces/HasWorldBorder.java @@ -0,0 +1,7 @@ +package fr.openmc.core.registry.worldtemplates.interfaces; + +public interface HasWorldBorder { + double[] getCenter(); + double getSize(); + +} diff --git a/src/main/java/fr/openmc/core/utils/bukkit/ParticleUtils.java b/src/main/java/fr/openmc/core/utils/bukkit/ParticleUtils.java index b48913293..97604acb9 100644 --- a/src/main/java/fr/openmc/core/utils/bukkit/ParticleUtils.java +++ b/src/main/java/fr/openmc/core/utils/bukkit/ParticleUtils.java @@ -6,6 +6,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion; import fr.openmc.core.OMCPlugin; import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.core.particles.ParticleOptions; import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.phys.Vec3; @@ -16,10 +17,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; @@ -45,7 +43,7 @@ public class ParticleUtils { PARTICLE_FALLBACKS.put("shriek",() -> 0); PARTICLE_FALLBACKS.put("entity_effect", () -> Color.WHITE); PARTICLE_FALLBACKS.put("tinted_leaves", () -> Color.WHITE); - PARTICLE_FALLBACKS.put("flash", () -> Color.WHITE); + PARTICLE_FALLBACKS.put("flash", () -> Color.RED); PARTICLE_FALLBACKS.put("effect", () -> new Particle.Spell(Color.WHITE, 1.0f)); PARTICLE_FALLBACKS.put("instant_effect", () -> new Particle.Spell(Color.WHITE, 1.0f)); PARTICLE_FALLBACKS.put("vibration", () -> new Vibration( @@ -63,21 +61,18 @@ public class ParticleUtils { PARTICLE_FALLBACKS.put("geyser_poof", () -> new Particle.GeyserBase(1, 1.0f)); } - public static void sendRandomCubeParticles(Player player, Particle particle, double radius, int amount) { - Location center = player.getLocation(); - - for (int i = 0; i < amount; i++) { - double x = (Math.random() * 2 - 1) * radius; // de -radius à +radius - double y = (Math.random() * 2 - 1) * radius; - double z = (Math.random() * 2 - 1) * radius; + public static void sendParticlePacket(Particle particle, Location loc, int radius) { + sendParticlePacket(particle, loc, loc.getNearbyEntitiesByType(Player.class, radius)); + } - Location loc = center.clone().add(x, y, z); - sendParticlePacket(player, particle, loc); - } + public static void sendParticlePacket(Particle particle, Location loc, int radius, T data) { + sendParticlePacket(particle, loc, loc.getNearbyEntitiesByType(Player.class, radius), data); } - public static void sendParticlePacket(Particle particle, Location loc, int radius) { - sendParticlePacket(particle, loc, loc.getNearbyEntitiesByType(Player.class, radius)); + public static void sendParticlePacket(Particle particle, Location loc, Collection receivers, T data) { + for (Player player : receivers) { + sendParticlePacket(player, particle, loc, 3, 0.2f, 0.2f, 0.2f, 0.01f, data); + } } public static void sendParticlePacket(Particle particle, Location loc, Collection receivers) { @@ -91,8 +86,32 @@ public static void sendParticlePacket(Player player, Particle particle, Location } public static void sendParticlePacket(Collection receivers, Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double speed, T data) { + Object resolvedData; + if (data != null) { + resolvedData = data; + } else { + Supplier fallback = PARTICLE_FALLBACKS.get(particle.getKey().getKey()); + if (fallback != null) { + resolvedData = fallback.get(); + } else { + resolvedData = null; + } + } + + ParticleOptions particleParam = CraftParticle.createParticleParam(particle, resolvedData); + + ClientboundLevelParticlesPacket packet = new ClientboundLevelParticlesPacket( + particleParam, + false, + false, + location.x(), location.y(), location.z(), + (float) offsetX, (float) offsetY, (float) offsetZ, + (float) speed, + count + ); + for (Player player : receivers) { - sendParticlePacket(player, particle, location, count, offsetX, offsetY, offsetZ, speed, data); + ((CraftPlayer) player).getHandle().connection.send(packet); } } @@ -127,6 +146,20 @@ public static void sendParticlePacket(Player player, Particle particle, Loca nmsPlayer.connection.send(packet); } + public static void spawnParticlesInCube(Location origin, Particle particle, int count, int size, T data) { + Collection receivers = origin.getNearbyEntitiesByType(Player.class, 64); + if (receivers.isEmpty()) return; + + for (int i = 0; i < count; i++) { + double x = (Math.random() * 2 - 1) * size; + double y = (Math.random() * 2 - 1) * size; + double z = (Math.random() * 2 - 1) * size; + + Location loc = origin.clone().add(x, y, z); + sendParticlePacket(receivers, particle, loc, 3, 0.2f, 0.2f, 0.2f, 0.01f, data); + } + } + public static void spawnRisingDustParticle(String regionId, World world, Location origin, Color color, float size, int steps, int count) { RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world)); if (regionManager == null) return; @@ -218,6 +251,99 @@ public static void spawnConvergingParticles(Location target, int count) { } } + public static void spawnConvergingParticlesSpherical(Location target, Particle particle, int count, double radius, double radiusPlayer, int durationTicks, T data) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + + List startPoints = new ArrayList<>(count); + + for (int i = 0; i < count; i++) { + double theta = random.nextDouble() * 2 * Math.PI; + double phi = Math.acos(2 * random.nextDouble() - 1); + + double x = radius * Math.sin(phi) * Math.cos(theta); + double y = radius * Math.cos(phi); + double z = radius * Math.sin(phi) * Math.sin(theta); + + startPoints.add(target.clone().add(x, y, z)); + } + + new BukkitRunnable() { + int tick = 0; + + @Override + public void run() { + if (tick > durationTicks) { + cancel(); + return; + } + + Collection receivers = target.getNearbyEntitiesByType(Player.class, radiusPlayer); + if (!receivers.isEmpty()) { + double progress = (double) tick / durationTicks; + + for (Location start : startPoints) { + double x = start.getX() + (target.getX() - start.getX()) * progress; + double y = start.getY() + (target.getY() - start.getY()) * progress; + double z = start.getZ() + (target.getZ() - start.getZ()) * progress; + + Location point = new Location(target.getWorld(), x, y, z); + + sendParticlePacket(receivers, particle, point, 1, 0.0, 0.0, 0.0, 0.0, data); + } + } + + tick++; + } + }.runTaskTimer(OMCPlugin.getInstance(), 0L, 1L); + } + + public static void spawnRepulsedParticlesSpherical(Location target, Particle particle, int count, double radius, double radiusPlayer, int durationTicks, T data) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + + List endPoints = new ArrayList<>(count); + + for (int i = 0; i < count; i++) { + double theta = random.nextDouble() * 2 * Math.PI; + double phi = Math.acos(2 * random.nextDouble() - 1); + + double x = radius * Math.sin(phi) * Math.cos(theta); + double y = radius * Math.cos(phi); + double z = radius * Math.sin(phi) * Math.sin(theta); + + endPoints.add(target.clone().add(x, y, z)); + } + + new BukkitRunnable() { + int tick = 0; + + @Override + public void run() { + if (tick > durationTicks) { + cancel(); + return; + } + + Collection receivers = target.getNearbyEntitiesByType(Player.class, radiusPlayer); + if (!receivers.isEmpty()) { + double progress = (double) tick / durationTicks; + + for (Location end : endPoints) { + double x = target.getX() + (end.getX() - target.getX()) * progress; + double y = target.getY() + (end.getY() - target.getY()) * progress; + double z = target.getZ() + (end.getZ() - target.getZ()) * progress; + + Location point = new Location(target.getWorld(), x, y, z); + + + sendParticlePacket(receivers, particle, point, 1, 0.0, 0.0, 0.0, 0.0, data); + } + } + + tick++; + } + }.runTaskTimer(OMCPlugin.getInstance(), 0L, 1L); + } + public static void spawnDispersingParticles(Location target, Particle particle, int count, int radius, double speed, T data) { Collection players = target.getNearbyEntitiesByType(Player.class, radius); diff --git a/src/main/java/fr/openmc/core/utils/nms/PlayerBiomeNMS.java b/src/main/java/fr/openmc/core/utils/nms/player/PlayerBiomeNMS.java similarity index 99% rename from src/main/java/fr/openmc/core/utils/nms/PlayerBiomeNMS.java rename to src/main/java/fr/openmc/core/utils/nms/player/PlayerBiomeNMS.java index 46df5530f..1013b575a 100644 --- a/src/main/java/fr/openmc/core/utils/nms/PlayerBiomeNMS.java +++ b/src/main/java/fr/openmc/core/utils/nms/player/PlayerBiomeNMS.java @@ -1,4 +1,4 @@ -package fr.openmc.core.utils.nms; +package fr.openmc.core.utils.nms.player; import fr.openmc.core.bootstrap.integration.OMCLogger; import net.minecraft.core.Holder; diff --git a/src/main/java/fr/openmc/core/utils/nms/PlayerPositionNMS.java b/src/main/java/fr/openmc/core/utils/nms/player/PlayerPositionNMS.java similarity index 96% rename from src/main/java/fr/openmc/core/utils/nms/PlayerPositionNMS.java rename to src/main/java/fr/openmc/core/utils/nms/player/PlayerPositionNMS.java index 321e223e2..eef55286f 100644 --- a/src/main/java/fr/openmc/core/utils/nms/PlayerPositionNMS.java +++ b/src/main/java/fr/openmc/core/utils/nms/player/PlayerPositionNMS.java @@ -1,4 +1,4 @@ -package fr.openmc.core.utils.nms; +package fr.openmc.core.utils.nms.player; import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; import net.minecraft.server.level.ServerPlayer; diff --git a/src/main/java/fr/openmc/core/utils/nms/PlayerRespawnNMS.java b/src/main/java/fr/openmc/core/utils/nms/player/PlayerRespawnNMS.java similarity index 99% rename from src/main/java/fr/openmc/core/utils/nms/PlayerRespawnNMS.java rename to src/main/java/fr/openmc/core/utils/nms/player/PlayerRespawnNMS.java index ca76eb435..e129cd262 100644 --- a/src/main/java/fr/openmc/core/utils/nms/PlayerRespawnNMS.java +++ b/src/main/java/fr/openmc/core/utils/nms/player/PlayerRespawnNMS.java @@ -1,4 +1,4 @@ -package fr.openmc.core.utils.nms; +package fr.openmc.core.utils.nms.player; import fr.openmc.core.OMCPlugin; import fr.openmc.core.registry.ambient.CustomAmbient; diff --git a/src/main/java/fr/openmc/core/utils/nms/PlayerSetTimeNMS.java b/src/main/java/fr/openmc/core/utils/nms/player/PlayerSetTimeNMS.java similarity index 97% rename from src/main/java/fr/openmc/core/utils/nms/PlayerSetTimeNMS.java rename to src/main/java/fr/openmc/core/utils/nms/player/PlayerSetTimeNMS.java index 660a58a20..8ee9abf9f 100644 --- a/src/main/java/fr/openmc/core/utils/nms/PlayerSetTimeNMS.java +++ b/src/main/java/fr/openmc/core/utils/nms/player/PlayerSetTimeNMS.java @@ -1,4 +1,4 @@ -package fr.openmc.core.utils.nms; +package fr.openmc.core.utils.nms.player; import net.minecraft.core.Holder; import net.minecraft.core.Registry; diff --git a/src/main/java/fr/openmc/core/utils/nms/PlayerWeatherNMS.java b/src/main/java/fr/openmc/core/utils/nms/player/PlayerWeatherNMS.java similarity index 94% rename from src/main/java/fr/openmc/core/utils/nms/PlayerWeatherNMS.java rename to src/main/java/fr/openmc/core/utils/nms/player/PlayerWeatherNMS.java index 89720ba6d..996ed3088 100644 --- a/src/main/java/fr/openmc/core/utils/nms/PlayerWeatherNMS.java +++ b/src/main/java/fr/openmc/core/utils/nms/player/PlayerWeatherNMS.java @@ -1,5 +1,6 @@ -package fr.openmc.core.utils.nms; +package fr.openmc.core.utils.nms.player; +import fr.openmc.core.utils.nms.WeatherType; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.server.level.ServerPlayer; import org.bukkit.craftbukkit.entity.CraftPlayer; diff --git a/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java b/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java index c0443cd71..420303065 100644 --- a/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java +++ b/src/test/java/fr/openmc/core/features/economy/EconomyFormattingTest.java @@ -30,7 +30,7 @@ void testFormat_SmallNumber() { @DisplayName("Format thousands with k suffix") void testFormat_Thousands() { String result = EconomyManager.getFormattedSimplifiedNumber(1500); - Assertions.assertEquals("1.5k", result); + Assertions.assertEquals("1,5k", result); } @Test @@ -55,7 +55,7 @@ void testFormat_Billions() { @DisplayName("Format with decimal truncation") void testFormat_Decimal() { String result = EconomyManager.getFormattedSimplifiedNumber(2_500_000); - Assertions.assertEquals("2.5M", result); + Assertions.assertEquals("2,5M", result); } @Test