From e7c19bcf5577b8f4965350d77967a50cc8377a78 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Wed, 10 Oct 2018 08:46:30 +0100 Subject: [PATCH 1/2] Change speakers to use the SPacketCustomSound packet instead The method to register new SoundEvents is private, which means that few (if any) mods actually register them. Consequently, one could not use the speaker to play any modded sound, as they weren't registered on the server side. Using SPacketCustomSound does mean we can no longer determine if a sound exists or not, but I think a price I'm willing to pay in order to allow playing modded sounds. --- .../peripheral/speaker/SpeakerPeripheral.java | 133 ++++++++---------- 1 file changed, 56 insertions(+), 77 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index 65831aba3a..62900f559c 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -8,10 +8,11 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.ILuaContext; -import dan200.computercraft.api.lua.ILuaTask; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; +import net.minecraft.network.play.server.SPacketCustomSound; +import net.minecraft.server.MinecraftServer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; @@ -19,27 +20,27 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getString; import static dan200.computercraft.core.apis.ArgumentHelper.optReal; -public class SpeakerPeripheral implements IPeripheral { - private TileSpeaker m_speaker; +public class SpeakerPeripheral implements IPeripheral +{ + private final TileSpeaker m_speaker; private long m_clock; private long m_lastPlayTime; private int m_notesThisTick; public SpeakerPeripheral() { - m_clock = 0; - m_lastPlayTime = 0; - m_notesThisTick = 0; + this( null ); } - SpeakerPeripheral(TileSpeaker speaker) + SpeakerPeripheral( TileSpeaker speaker ) { - this(); + m_clock = 0; + m_lastPlayTime = 0; + m_notesThisTick = 0; m_speaker = speaker; } @@ -64,15 +65,9 @@ public BlockPos getPos() @Override public boolean equals( IPeripheral other ) { - if( other != null && other instanceof SpeakerPeripheral ) - { - SpeakerPeripheral otherSpeaker = (SpeakerPeripheral) other; - return otherSpeaker.m_speaker == m_speaker; - } - else - { - return false; - } + if( other == this ) return true; + if( !(other instanceof SpeakerPeripheral) ) return false; + return m_speaker == ((SpeakerPeripheral) other).m_speaker; } @Nonnull @@ -87,8 +82,8 @@ public String getType() public String[] getMethodNames() { return new String[] { - "playSound", // Plays sound at resourceLocator - "playNote" // Plays note + "playSound", // Plays sound at resourceLocator + "playNote" // Plays note }; } @@ -100,18 +95,22 @@ public Object[] callMethod( @Nonnull IComputerAccess computerAccess, @Nonnull IL // playSound case 0: { - return playSound(args, context, false); + String name = getString( args, 0 ); + float volume = (float) optReal( args, 1, 1.0 ); + float pitch = (float) optReal( args, 2, 1.0 ); + + return new Object[] { playSound( context, name, volume, pitch, false ) }; } // playNote case 1: { - return playNote(args, context); + return playNote( args, context ); } default: { - throw new LuaException("Method index out of range!"); + throw new LuaException( "Method index out of range!" ); } } @@ -120,75 +119,55 @@ public Object[] callMethod( @Nonnull IComputerAccess computerAccess, @Nonnull IL @Nonnull private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException { - String name = getString(arguments, 0); + String name = getString( arguments, 0 ); float volume = (float) optReal( arguments, 1, 1.0 ); float pitch = (float) optReal( arguments, 2, 1.0 ); + + String noteName = "block.note." + name; - // Check if sound exists - if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + name ) ) ) + // Check if the note exists + if( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( noteName ) ) ) { - throw new LuaException("Invalid instrument, \"" + arguments[0] + "\"!"); + throw new LuaException( "Invalid instrument, \"" + name + "\"!" ); } // If the resource location for note block notes changes, this method call will need to be updated - Object[] returnValue = playSound( - new Object[] { - "block.note." + name, - (double)Math.min( volume, 3f ), - Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f) - }, context, true + boolean success = playSound( context, noteName, + Math.min( volume, 3f ), + (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true ); - if( returnValue[0] instanceof Boolean && (Boolean) returnValue[0] ) + if( success ) m_notesThisTick++; + return new Object[] { success }; + } + + private synchronized boolean playSound( ILuaContext context, String name, float volume, float pitch, boolean isNote ) throws LuaException + { + if( m_clock - m_lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS && + (!isNote || m_clock - m_lastPlayTime != 0 || m_notesThisTick >= ComputerCraft.maxNotesPerTick) ) { - m_notesThisTick++; + // Rate limiting occurs when we've already played a sound within the last tick, or we've + // played more notes than allowable within the current tick. + return false; } - return returnValue; - } + final World world = getWorld(); + final BlockPos pos = getPos(); - @Nonnull - private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException - { - String name = getString(arguments, 0); - float volume = (float) optReal( arguments, 1, 1.0 ); - float pitch = (float) optReal( arguments, 2, 1.0 ); + context.issueMainThreadTask( () -> { + MinecraftServer server = world.getMinecraftServer(); + if( server == null ) return null; - ResourceLocation resourceName = new ResourceLocation( name ); + double x = pos.getX() + 0.5, y = pos.getY() + 0.5, z = pos.getZ() + 0.5; + server.getPlayerList().sendToAllNearExcept( + null, x, y, z, volume > 1.0f ? 16 * volume : 16.0, world.provider.getDimension(), + new SPacketCustomSound( name, SoundCategory.RECORDS, x, y, z, volume, pitch ) + ); + return null; + } ); - if( m_clock - m_lastPlayTime >= TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS || ( ( m_clock - m_lastPlayTime == 0 ) && ( m_notesThisTick < ComputerCraft.maxNotesPerTick ) && isNote ) ) - { - if( SoundEvent.REGISTRY.containsKey(resourceName) ) - { - final World world = getWorld(); - final BlockPos pos = getPos(); - final ResourceLocation resource = resourceName; - final float vol = volume; - final float soundPitch = pitch; - - context.issueMainThreadTask(new ILuaTask() - { - @Nullable - @Override - public Object[] execute() throws LuaException - { - world.playSound( null, pos, SoundEvent.REGISTRY.getObject( resource ), SoundCategory.RECORDS, Math.min( vol, 3f ), soundPitch ); - return null; - } - }); - - m_lastPlayTime = m_clock; - return new Object[]{true}; // Success, return true - } - else - { - return new Object[]{false}; // Failed - sound not existent, return false - } - } - else - { - return new Object[]{false}; // Failed - rate limited, return false - } + m_lastPlayTime = m_clock; + return true; } } From 5694491124988e22a576e9d9c84ae14618f795ff Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 12 Oct 2018 12:05:00 +0100 Subject: [PATCH 2/2] Clamp the volume for all sounds, rather than just notes This restores the previous behaviour. --- .../peripheral/speaker/SpeakerPeripheral.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index 62900f559c..9ed9d82440 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -132,10 +132,7 @@ private synchronized Object[] playNote( Object[] arguments, ILuaContext context } // If the resource location for note block notes changes, this method call will need to be updated - boolean success = playSound( context, noteName, - Math.min( volume, 3f ), - (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true - ); + boolean success = playSound( context, noteName, volume, (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true ); if( success ) m_notesThisTick++; return new Object[] { success }; @@ -151,17 +148,18 @@ private synchronized boolean playSound( ILuaContext context, String name, float return false; } - final World world = getWorld(); - final BlockPos pos = getPos(); + World world = getWorld(); + BlockPos pos = getPos(); context.issueMainThreadTask( () -> { MinecraftServer server = world.getMinecraftServer(); if( server == null ) return null; double x = pos.getX() + 0.5, y = pos.getY() + 0.5, z = pos.getZ() + 0.5; + float adjVolume = Math.min( volume, 3.0f ); server.getPlayerList().sendToAllNearExcept( - null, x, y, z, volume > 1.0f ? 16 * volume : 16.0, world.provider.getDimension(), - new SPacketCustomSound( name, SoundCategory.RECORDS, x, y, z, volume, pitch ) + null, x, y, z, adjVolume > 1.0f ? 16 * adjVolume : 16.0, world.provider.getDimension(), + new SPacketCustomSound( name, SoundCategory.RECORDS, x, y, z, adjVolume, pitch ) ); return null; } );