Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CI Skip] Allow server owners to fully customize sounds #2844

Merged
merged 30 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
18cb30c
First draft for the sound service
TheBusyBiscuit Feb 26, 2021
f0beda9
Added a lot more sounds to the new system
TheBusyBiscuit Feb 26, 2021
f3c43f7
Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git in…
TheBusyBiscuit Feb 28, 2021
84fc597
Added even more sounds to the new system.
TheBusyBiscuit Feb 28, 2021
5dce733
Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git in…
TheBusyBiscuit Mar 2, 2021
f783ffe
Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git into
TheBusyBiscuit Mar 13, 2021
e0a58a5
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Mar 21, 2021
038a74f
Update CHANGELOG.md
TheBusyBiscuit Mar 21, 2021
f85c970
Re-enabled Unit Test
TheBusyBiscuit Mar 21, 2021
85745c5
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Mar 31, 2021
1a45f61
Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git into
TheBusyBiscuit Apr 8, 2021
ab04118
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Apr 10, 2021
dcc3afe
Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git into
TheBusyBiscuit Apr 23, 2021
2790db1
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Apr 24, 2021
2d18bb4
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Apr 25, 2021
1ca3ec3
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Apr 26, 2021
d550d88
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Apr 30, 2021
c9f0140
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit May 25, 2021
f27ba85
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Jun 4, 2021
2dbace3
Update CHANGELOG.md
TheBusyBiscuit Jun 4, 2021
a7ff271
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Jul 1, 2021
ef8b174
Some more work on this
TheBusyBiscuit Jul 1, 2021
1a9cfbf
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Jul 19, 2021
9840491
Merge branch 'master' into feature/sound-manager
svr333 Jan 24, 2022
8acff6a
Resolve build errors
svr333 Jan 24, 2022
05569fd
Merge branch 'master' into feature/sound-manager
TheBusyBiscuit Oct 17, 2022
f98ffe9
Merge branch 'master' into feature/sound-manager
WalshyDev Jun 12, 2023
ec4cfe3
Merge branch 'master' into feature/sound-manager
WalshyDev Jun 21, 2023
f9e7f26
Update CHANGELOG.md
WalshyDev Jun 21, 2023
43bdd9f
Merge branch 'master' into feature/sound-manager
WalshyDev Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#26
* (API) Added SlimefunItemSpawnEvent and ItemSpawnReason
* Added "Amethyst Block -> 4 Amethyst Shards" recipe to the Grind Stone
* Added an option to the IndustrialMiner to configure if they can mine deepslate ores
* Added sound effects for Slime Boots
* Added `sounds.yml` file to configure sound effects for Slimefun
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
* (API) Added `LimitedUseItem`

#### Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;

import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;

Expand Down Expand Up @@ -64,7 +64,7 @@ public void accept(PlayerProfile profile) {

if (!isInstant) {
Slimefun.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
SoundEffect.PLAYER_RESEARCHING_SOUND.playFor(p);
Slimefun.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER, research.getName(p)).replace("%progress%", "0%"));
}, 5L);
}
Expand Down Expand Up @@ -93,7 +93,7 @@ private void sendUpdateMessage(@Nonnull Player p) {
int index = i;

Slimefun.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1);
SoundEffect.PLAYER_RESEARCHING_SOUND.playFor(p);

Slimefun.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> {
String progress = RESEARCH_PROGRESS[index - 1] + "%";
Expand Down Expand Up @@ -125,5 +125,4 @@ private void onFinish(@Nonnull Player p) {
callback.accept(p);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ default void damageItem(@Nonnull Player p, @Nullable ItemStack item) {
Damageable damageable = (Damageable) meta;

if (damageable.getDamage() >= item.getType().getMaxDurability()) {
// No need for a SoundEffect equivalent here since this is supposed to be a vanilla sound.
p.playSound(p.getEyeLocation(), Sound.ENTITY_ITEM_BREAK, 1, 1);
item.setAmount(0);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.github.thebusybiscuit.slimefun4.core.services.sounds;

import javax.annotation.Nonnull;

/**
* This structure class holds configured values for a {@link SoundEffect}.
*
* @author TheBusyBiscuit
*
* @see SoundService
* @see SoundEffect
*
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
*/
public class SoundConfiguration {

private final String sound;
private final float volume;
private final float pitch;

protected SoundConfiguration(@Nonnull String sound, float volume, float pitch) {
this.sound = sound;
this.volume = volume;
this.pitch = pitch;
}

public @Nonnull String getSoundId() {
return sound;
}

public float getVolume() {
return volume;
}

public float getPitch() {
return pitch;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package io.github.thebusybiscuit.slimefun4.core.services.sounds;

import java.util.Locale;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.lang.Validate;
import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;

import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;

/**
* This enum holds references to all our sounds.
*
* @author TheBusyBiscuit
*
* @see SoundService
* @see SoundConfiguration
*
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
*/
public enum SoundEffect {

ARMOR_FORGE_FINISH_SOUND(Sound.BLOCK_ANVIL_USE, 1F, 1F),
ARMOR_FORGE_WORKING_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F),
AUTOMATED_PANNING_MACHINE_FAIL_SOUND(Sound.ENTITY_ARMOR_STAND_BREAK, 1F, 1F),
AUTOMATED_PANNING_MACHINE_SUCCESS_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F),
BEE_BOOTS_FALL_SOUND("block.honey_block.fall", 1F, 1F),
DIET_COOKIE_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F),
ENDER_BACKPACK_OPEN_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F),
ENHANCED_CRAFTING_TABLE_CRAFT_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F),
ELYTRA_CAP_IMPACT_SOUND(Sound.BLOCK_STONE_HIT, 1F, 1F),
EXPLOSIVE_BOW_HIT_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F),
FISHERMAN_ANDROID_FISHING_SOUND(Sound.ENTITY_PLAYER_SPLASH, 0.3F, 0.7F),
FLASK_OF_KNOWLEDGE_FILLUP_SOUND(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1F, 0.5F),
INFUSED_HOPPER_TELEPORT_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 0.5F, 2F),
INFUSED_MAGNET_TELEPORT_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 0.25F, 0.9F),
IRON_GOLEM_ASSEMBLER_ASSEMBLE_SOUND("entity.iron_golem.repair", 0.5F, 1F),
JETBOOTS_THRUST_SOUND(Sound.ENTITY_TNT_PRIMED, 0.25F, 1F),
JETPACK_THRUST_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 0.25F, 1F),
JUICER_USE_SOUND(Sound.ENTITY_PLAYER_SPLASH, 1F, 1F),
MAGICAL_EYE_OF_ENDER_USE_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F),
MAGIC_SUGAR_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F),
PLAYER_RESEARCHING_SOUND(Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F),
PORTABLE_CRAFTER_OPEN_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F),
PRESSURE_CHAMBER_FINISH_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F),
PRESSURE_CHAMBER_WORKING_SOUND(Sound.ENTITY_TNT_PRIMED, 1F, 1F),
SLIME_BOOTS_FALL_SOUND(Sound.BLOCK_SLIME_BLOCK_FALL, 1F, 1F),
SMELTERY_CRAFT_SOUND(Sound.BLOCK_LAVA_POP, 1F, 1F),
SOULBOUND_RUNE_RITUAL_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 0.3F, 1F),
SPLINT_CONSUME_SOUND(Sound.ENTITY_SKELETON_HURT, 1F, 1F),
TOME_OF_KNOWLEDGE_USE_SOUND(Sound.ENTITY_PLAYER_LEVELUP, 1F, 1F),
TRASH_CAN_OPEN_SOUND(Sound.BLOCK_ANVIL_LAND, 1F, 1F),
VAMPIRE_BLADE_HEALING_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 0.7F, 0.7F),
VILLAGER_RUNE_TRANSFORM_SOUND(Sound.ENTITY_VILLAGER_CELEBRATE, 1F, 1.4F),
VITAMINS_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F),
WIND_STAFF_USE_SOUND(Sound.ENTITY_TNT_PRIMED, 1F, 1F);

private final String defaultSound;
private final float defaultVolume;
private final float defaultPitch;

SoundEffect(@Nonnull String sound, float volume, float pitch) {
Validate.notNull(sound, "The Sound id cannot be null!");
Validate.isTrue(volume >= 0, "The volume cannot be a negative number.");
Validate.isTrue(pitch >= 0.5, "A pitch below 0.5 has no effect on the sound.");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved

this.defaultSound = sound;
this.defaultVolume = volume;
this.defaultPitch = pitch;
}

SoundEffect(@Nonnull Sound sound, float volume, float pitch) {
Validate.notNull(sound, "The Sound id cannot be null!");
Validate.isTrue(volume >= 0, "The volume cannot be a negative number.");
Validate.isTrue(pitch >= 0.5, "A pitch below 0.5 has no effect on the sound.");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved

/*
* Only Minecraft 1.16+ implements Keyed for Sound.
* So we need to check this first.
*/
if (sound instanceof Keyed) {
this.defaultSound = sound.getKey().getKey();
} else {
this.defaultSound = sound.name().toLowerCase(Locale.ROOT).replace('_', '.');
}
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved

this.defaultVolume = volume;
this.defaultPitch = pitch;
}

private @Nullable SoundConfiguration getConfiguration() {
SoundConfiguration config = Slimefun.getSoundService().getConfiguration(this);

if (config == null) {
// This should not happen. But if it does... send a warning
Slimefun.logger().log(Level.WARNING, "Could not find any sound configuration for: {0}", name());
}

return config;
}

/**
* This method will play this {@link SoundEffect} only to the given {@link Player} using the
* eye {@link Location} of the {@link Player} and the {@link SoundCategory} {@code PLAYERS}.
*
* @param player
* The {@link Player} which to play the {@link Sound} to.
*/
public void playFor(@Nonnull Player player) {
Validate.notNull(player, "Cannot play sounds to a Player that is null!");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
SoundConfiguration config = getConfiguration();

if (config != null) {
Location loc = player.getEyeLocation();
player.playSound(loc, config.getSoundId(), SoundCategory.PLAYERS, config.getVolume(), config.getPitch());
}
}

/**
* This method will play this {@link SoundEffect} at the given {@link Location} using the
* provided {@link SoundCategory}.
*
* @param loc
* The {@link Location} at which to play the {@link SoundEffect}.
* @param category
* The {@link SoundCategory} that should be used.
*/
public void playAt(@Nonnull Location loc, @Nonnull SoundCategory category) {
Validate.notNull(loc, "The location should not be null.");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
SoundConfiguration config = getConfiguration();

if (config != null) {
loc.getWorld().playSound(loc, config.getSoundId(), category, config.getVolume(), config.getPitch());
}
}

/**
* This method will play this {@link SoundEffect} at the {@link Location} of the given {@link Block},
* the used {@link SoundCategory} will be {@code BLOCKS}.
*
* @param block
* The {@link Block} at which to play the {@link SoundEffect}
*/
public void playAt(@Nonnull Block block) {
Validate.notNull(block, "The block cannot be null.");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
playAt(block.getLocation(), SoundCategory.BLOCKS);
}

/**
* This returns the default sound id.
*
* @return The default sound id.
*/
public @Nonnull String getDefaultSoundId() {
return defaultSound;
}

/**
* This returns the default volume.
*
* @return The default volume.
*/
float getDefaultVolume() {
return defaultVolume;
}

/**
* This returns the default pitch.
*
* @return The default pitch.
*/
float getDefaultPitch() {
return defaultPitch;
}
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package io.github.thebusybiscuit.slimefun4.core.services.sounds;

import java.util.EnumMap;
import java.util.Map;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.lang.Validate;

import io.github.bakedlibs.dough.config.Config;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;

/**
* The {@link SoundService} is responsible for our sound management.
* It allows server owners to fully customize their users' sound experience.
*
* @author TheBusyBiscuit
*
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
*/
public class SoundService {

/**
* Our {@link Config} instance.
*/
private final Config config;

/**
* In this map we cache the corresponding {@link SoundConfiguration} to each {@link SoundEffect}.
*/
private final Map<SoundEffect, SoundConfiguration> soundMap = new EnumMap<>(SoundEffect.class);

public SoundService(@Nonnull Slimefun plugin) {
config = new Config(plugin, "sounds.yml");

// @formatter:off
config.getConfiguration().options().header(
"This file is used to assign the sounds which Slimefun will play." +
"\nYou can fully customize any sound you want and even change their pitch" +
"\nand volume. To disable a sound, simply set the volume to zero."
);
// @formatter:on

config.getConfiguration().options().copyHeader(true);
}

/**
* This method reloads every {@link SoundConfiguration}.
*
* @param save
* Whether to save the defaults to disk
*/
public void reload(boolean save) {
config.reload();

for (SoundEffect sound : SoundEffect.values()) {
try {
reloadSound(sound);
} catch (Exception | LinkageError x) {
Slimefun.logger().log(Level.SEVERE, x, () -> "An exception was thrown while trying to load the configuration data for the following sound:" + sound.name());
}
}

if (save) {
config.save();
}
}

private void reloadSound(@Nonnull SoundEffect sound) {
// Set up default values
config.setDefaultValue(sound.name() + ".sound", sound.getDefaultSoundId());
config.setDefaultValue(sound.name() + ".volume", sound.getDefaultVolume());
config.setDefaultValue(sound.name() + ".pitch", sound.getDefaultPitch());

// Read the values
String soundId = config.getString(sound.name() + ".sound");
float volume = config.getFloat(sound.name() + ".volume");
float pitch = config.getFloat(sound.name() + ".pitch");

// Check whether the volume is at least 0.0
if (volume < 0) {
Slimefun.logger().log(Level.WARNING, "Invalid value in sounds.yml! Volume for Sound \"{0}\" was {1} (must be at least 0.0)", new Object[] { sound.name(), volume });
volume = 0;
}

// Check if the pitch is at least 0.5
if (pitch < 0.5F) {
Slimefun.logger().log(Level.WARNING, "Invalid value in sounds.yml! Pitch for Sound \"{0}\" was {1} (must be at least 0.5)", new Object[] { sound.name(), pitch });
pitch = 0.5F;
}

// Cache this configuration
SoundConfiguration configuration = new SoundConfiguration(soundId, volume, pitch);
soundMap.put(sound, configuration);
}

/**
* This returns the currently used (immutable) {@link SoundConfiguration} for the given {@link SoundEffect}.
*
* @param sound
* The {@link SoundEffect}
*
* @return The corresponding {@link SoundConfiguration}. This may be null if something went wrong
*/
public @Nullable SoundConfiguration getConfiguration(@Nonnull SoundEffect sound) {
Validate.notNull(sound, "The sound must not be null!");
WalshyDev marked this conversation as resolved.
Show resolved Hide resolved
return soundMap.get(sound);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* This package holds classes related to the
* {@link io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundService}.
* This service is responsible for our sound management and allowing server owners to fully customize
* their sound experience.
*/
package io.github.thebusybiscuit.slimefun4.core.services.sounds;
Loading