ViaRewind-Legacy-Support/src/main/java/com/viaversion/viarewind/legacysupport/listener/SoundListener.java

210 lines
8.5 KiB
Java

/*
* This file is part of ViaRewind-Legacy-Support - https://github.com/ViaVersion/ViaRewind-Legacy-Support
* Copyright (C) 2018-2024 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.legacysupport.listener;
import com.viaversion.viarewind.legacysupport.reflection.MethodSignature;
import com.viaversion.viarewind.legacysupport.reflection.ReflectionAPI;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viarewind.legacysupport.BukkitPlugin;
import com.viaversion.viarewind.legacysupport.injector.NMSReflection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.PlayerExpChangeEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import java.lang.reflect.Method;
@SuppressWarnings("unchecked")
public class SoundListener implements Listener {
private static boolean isSoundCategory = false;
static {
try {
Class.forName("org.bukkit.SoundCategory");
isSoundCategory = true;
} catch (ClassNotFoundException ignored) {
}
}
public SoundListener(final BukkitPlugin plugin) {
try {
Class.forName("org.bukkit.event.entity.EntityPickupItemEvent");
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onItemPickUp(EntityPickupItemEvent e) {
if (!(e.getEntity() instanceof Player)) return;
SoundListener.this.onItemPickUp((Player) e.getEntity());
}
}, plugin);
} catch (Exception ex) {
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onItemPickUp(PlayerPickupItemEvent e) {
SoundListener.this.onItemPickUp(e.getPlayer());
}
}, plugin);
}
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent e) {
final Player player = e.getPlayer();
if (Via.getAPI().getPlayerVersion(player) >= ProtocolVersion.v1_9.getVersion()) return;
if (Via.getAPI().getServerVersion().lowestSupportedVersion() >= ProtocolVersion.v1_17.getVersion()) {
player.playSound(e.getBlockPlaced().getLocation(), e.getBlock().getBlockData().getSoundGroup().getPlaceSound(), 1.0f, 0.8f);
} else {
playBlockPlaceSoundNMS(player, e.getBlock());
}
}
private void onItemPickUp(Player player) {
final float volume = 0.2f;
final float pitch = (float) ((Math.random() - Math.random()) * 0.7f + 1.0f) * 2.0f;
playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, volume, pitch);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onExperienceOrbPickup(PlayerExpChangeEvent e) {
final float volume = 0.1f;
final float pitch = (float) (0.5f * ((Math.random() - Math.random()) * 0.7f + 1.8f));
playSound(e.getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, volume, pitch);
}
private static void playSound(final Location loc, final Sound sound, final float volume, final float pitch) {
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.getWorld() == loc.getWorld())
.filter(p -> p.getLocation().distanceSquared(loc) < (double) 16 * (double) 16)
.filter(p -> Via.getAPI().getPlayerVersion(p) <= 47)
.forEach(p -> {
if (isSoundCategory) {
p.playSound(loc, sound, SoundCategory.valueOf("PLAYERS"), volume, pitch);
} else {
p.playSound(loc, sound, volume, pitch);
}
});
}
// 1.8.8 -> 1.16.5
private static void playBlockPlaceSoundNMS(Player player, Block block) {
try {
World world = block.getWorld();
Object nmsWorld = world.getClass().getMethod("getHandle").invoke(world);
Class<?> blockPositionClass = NMSReflection.getBlockPositionClass();
Object blockPosition = null;
if (blockPositionClass != null)
blockPosition = blockPositionClass.getConstructor(int.class, int.class, int.class).newInstance(block.getX(), block.getY(), block.getZ());
Method getTypeMethod = nmsWorld.getClass().getMethod("getType", blockPositionClass);
getTypeMethod.setAccessible(true);
Object blockData = getTypeMethod.invoke(nmsWorld, blockPosition);
Method getBlock = blockData.getClass().getMethod("getBlock");
getBlock.setAccessible(true);
Object nmsBlock = getBlock.invoke(blockData);
Method getStepSound = ReflectionAPI.pickMethod(
nmsBlock.getClass(),
new MethodSignature("w"), // 1.9 -> 1.10
new MethodSignature("getStepSound", blockData.getClass()), // 1.14 -> 1.16
new MethodSignature("getStepSound") // 1.11 -> 1.12
);
getStepSound.setAccessible(true);
Object soundType;
if (getStepSound.getParameterCount() == 0) {
soundType = getStepSound.invoke(nmsBlock); // 1.9 -> 1.13
} else {
soundType = getStepSound.invoke(nmsBlock, blockData); // 1.14 -> 1.16.5
}
Method soundEffectMethod;
Method volumeMethod;
Method pitchMethod;
try {
// 1.16.5
soundEffectMethod = soundType.getClass().getMethod("getPlaceSound");
volumeMethod = soundType.getClass().getMethod("getVolume");
pitchMethod = soundType.getClass().getMethod("getPitch");
} catch (NoSuchMethodException ex) {
// 1.9 -> 1.16.4
soundEffectMethod = soundType.getClass().getMethod("e");
volumeMethod = soundType.getClass().getMethod("a");
pitchMethod = soundType.getClass().getMethod("b");
}
Object soundEffect = soundEffectMethod.invoke(soundType);
float volume = (float) volumeMethod.invoke(soundType);
float pitch = (float) pitchMethod.invoke(soundType);
Object soundCategory = Enum.valueOf(NMSReflection.getSoundCategoryClass(), "BLOCKS");
volume = (volume + 1.0f) / 2.0f;
pitch *= 0.8;
playSound(player, soundEffect, soundCategory, block.getX() + 0.5, block.getY() + 0.5, block.getZ() + 0.5, volume, pitch);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 1.8.8 -> 1.16.5
private static void playSound(Player player, Object soundEffect, Object soundCategory, double x, double y, double z, float volume, float pitch) {
try {
Object packet = NMSReflection.getGamePacketClass("PacketPlayOutNamedSoundEffect").getConstructor(
soundEffect.getClass(), soundCategory.getClass(),
double.class, double.class, double.class,
float.class, float.class
).newInstance(
soundEffect, soundCategory,
x, y, z,
volume, pitch
);
// Volume = 1
// Pitch = .8
NMSReflection.sendPacket(player, packet);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}