mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-28 08:12:18 +00:00
fix: another part of syncing with Tweaked codebase
This commit is contained in:
parent
d4f1e34023
commit
861a9e199d
@ -75,6 +75,8 @@ public final class ComputerCraft implements ModInitializer
|
||||
) );
|
||||
public static int httpMaxRequests = 16;
|
||||
public static int httpMaxWebsockets = 4;
|
||||
public static int httpDownloadBandwidth = 32 * 1024 * 1024;
|
||||
public static int httpUploadBandwidth = 32 * 1024 * 1024;
|
||||
|
||||
public static boolean enableCommandBlock = false;
|
||||
public static int modemRange = 64;
|
||||
|
84
src/main/java/dan200/computercraft/client/SoundManager.java
Normal file
84
src/main/java/dan200/computercraft/client/SoundManager.java
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.sound.AbstractSoundInstance;
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
import net.minecraft.client.sound.TickableSoundInstance;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SoundManager
|
||||
{
|
||||
private static final Map<UUID, MoveableSound> sounds = new HashMap<>();
|
||||
|
||||
public static void playSound(UUID source, Vec3d position, SoundEvent event, float volume, float pitch )
|
||||
{
|
||||
var soundManager = MinecraftClient.getInstance().getSoundManager();
|
||||
|
||||
MoveableSound oldSound = sounds.get( source );
|
||||
if( oldSound != null ) soundManager.stop( oldSound );
|
||||
|
||||
MoveableSound newSound = new MoveableSound( event, position, volume, pitch );
|
||||
sounds.put( source, newSound );
|
||||
soundManager.play( newSound );
|
||||
}
|
||||
|
||||
public static void stopSound( UUID source )
|
||||
{
|
||||
SoundInstance sound = sounds.remove( source );
|
||||
if( sound == null ) return;
|
||||
|
||||
MinecraftClient.getInstance().getSoundManager().stop( sound );
|
||||
}
|
||||
|
||||
public static void moveSound( UUID source, Vec3d position )
|
||||
{
|
||||
MoveableSound sound = sounds.get( source );
|
||||
if( sound != null ) sound.setPosition( position );
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
sounds.clear();
|
||||
}
|
||||
|
||||
private static class MoveableSound extends AbstractSoundInstance implements TickableSoundInstance
|
||||
{
|
||||
protected MoveableSound( SoundEvent sound, Vec3d position, float volume, float pitch )
|
||||
{
|
||||
super( sound, SoundCategory.RECORDS );
|
||||
setPosition( position );
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
attenuationType = SoundInstance.AttenuationType.LINEAR;
|
||||
}
|
||||
|
||||
void setPosition( Vec3d position )
|
||||
{
|
||||
x = (float) position.getX();
|
||||
y = (float) position.getY();
|
||||
z = (float) position.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -62,6 +62,7 @@ public interface IAPIEnvironment
|
||||
@Nullable
|
||||
IPeripheral getPeripheral( ComputerSide side );
|
||||
|
||||
@Nullable
|
||||
String getLabel();
|
||||
|
||||
void setLabel( @Nullable String label );
|
||||
|
@ -96,9 +96,8 @@ public abstract class HandleGeneric
|
||||
|
||||
protected static SeekableByteChannel asSeekable( Channel channel )
|
||||
{
|
||||
if( !(channel instanceof SeekableByteChannel) ) return null;
|
||||
if( !(channel instanceof SeekableByteChannel seekable) ) return null;
|
||||
|
||||
SeekableByteChannel seekable = (SeekableByteChannel) channel;
|
||||
try
|
||||
{
|
||||
seekable.position( seekable.position() );
|
||||
|
@ -20,6 +20,8 @@ import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
import io.netty.handler.traffic.AbstractTrafficShapingHandler;
|
||||
import io.netty.handler.traffic.GlobalTrafficShapingHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.ssl.SSLException;
|
||||
@ -28,9 +30,7 @@ import javax.net.ssl.TrustManagerFactory;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -38,10 +38,8 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public final class NetworkUtils
|
||||
{
|
||||
public static final ExecutorService EXECUTOR = new ThreadPoolExecutor(
|
||||
4, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<>(),
|
||||
public static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(
|
||||
4,
|
||||
ThreadUtils.builder( "Network" )
|
||||
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
||||
.build()
|
||||
@ -52,6 +50,15 @@ public final class NetworkUtils
|
||||
.build()
|
||||
);
|
||||
|
||||
public static final AbstractTrafficShapingHandler SHAPING_HANDLER = new GlobalTrafficShapingHandler(
|
||||
EXECUTOR, ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth
|
||||
);
|
||||
|
||||
static
|
||||
{
|
||||
EXECUTOR.setKeepAliveTime( 60, TimeUnit.SECONDS );
|
||||
}
|
||||
|
||||
private NetworkUtils()
|
||||
{
|
||||
}
|
||||
@ -107,8 +114,18 @@ public final class NetworkUtils
|
||||
}
|
||||
}
|
||||
|
||||
public static void reloadConfig()
|
||||
{
|
||||
SHAPING_HANDLER.configure( ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth );
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
SHAPING_HANDLER.trafficCounter().resetCumulativeTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link InetSocketAddress} from a {@link URI}.
|
||||
* Create a {@link InetSocketAddress} from a {@link java.net.URI}.
|
||||
*
|
||||
* Note, this may require a DNS lookup, and so should not be executed on the main CC thread.
|
||||
*
|
||||
|
@ -167,6 +167,7 @@ public class HttpRequest extends Resource<HttpRequest>
|
||||
}
|
||||
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast( NetworkUtils.SHAPING_HANDLER );
|
||||
if( sslContext != null )
|
||||
{
|
||||
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
|
||||
|
@ -100,9 +100,8 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
||||
{
|
||||
if( closed || request.checkClosed() ) return;
|
||||
|
||||
if( message instanceof HttpResponse )
|
||||
if( message instanceof HttpResponse response )
|
||||
{
|
||||
HttpResponse response = (HttpResponse) message;
|
||||
|
||||
if( request.redirects.get() > 0 )
|
||||
{
|
||||
@ -137,9 +136,8 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
||||
responseHeaders.add( response.headers() );
|
||||
}
|
||||
|
||||
if( message instanceof HttpContent )
|
||||
if( message instanceof HttpContent content )
|
||||
{
|
||||
HttpContent content = (HttpContent) message;
|
||||
|
||||
if( responseBody == null )
|
||||
{
|
||||
@ -162,9 +160,8 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
||||
responseBody.addComponent( true, partial.retain() );
|
||||
}
|
||||
|
||||
if( message instanceof LastHttpContent )
|
||||
if( message instanceof LastHttpContent last )
|
||||
{
|
||||
LastHttpContent last = (LastHttpContent) message;
|
||||
responseHeaders.add( last.trailingHeaders() );
|
||||
|
||||
// Set the content length, if not already given.
|
||||
|
@ -22,6 +22,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
||||
@ -145,20 +146,22 @@ public class Websocket extends Resource<Websocket>
|
||||
protected void initChannel( SocketChannel ch )
|
||||
{
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast( NetworkUtils.SHAPING_HANDLER );
|
||||
if( sslContext != null )
|
||||
{
|
||||
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
|
||||
}
|
||||
|
||||
String subprotocol = headers.get( HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL );
|
||||
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
|
||||
uri, WebSocketVersion.V13, null, true, headers,
|
||||
uri, WebSocketVersion.V13, subprotocol, true, headers,
|
||||
options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage
|
||||
);
|
||||
|
||||
p.addLast(
|
||||
new HttpClientCodec(),
|
||||
new HttpObjectAggregator( 8192 ),
|
||||
WebSocketClientCompressionHandler.INSTANCE,
|
||||
WebsocketCompressionHandler.INSTANCE,
|
||||
new WebsocketHandler( Websocket.this, handshaker, options )
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.core.apis.http.websocket;
|
||||
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameClientExtensionHandshaker;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateClientExtensionHandshaker;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
|
||||
|
||||
import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker.MAX_WINDOW_SIZE;
|
||||
|
||||
/**
|
||||
* An alternative to {@link WebSocketClientCompressionHandler} which supports the {@literal client_no_context_takeover}
|
||||
* extension. Makes CC <em>slightly</em> more flexible.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
final class WebsocketCompressionHandler extends WebSocketClientExtensionHandler
|
||||
{
|
||||
public static final WebsocketCompressionHandler INSTANCE = new WebsocketCompressionHandler();
|
||||
|
||||
private WebsocketCompressionHandler()
|
||||
{
|
||||
super(
|
||||
new PerMessageDeflateClientExtensionHandshaker(
|
||||
6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE,
|
||||
true, false
|
||||
),
|
||||
new DeflateFrameClientExtensionHandshaker( false ),
|
||||
new DeflateFrameClientExtensionHandshaker( true )
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -55,9 +55,8 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
|
||||
return;
|
||||
}
|
||||
|
||||
if( msg instanceof FullHttpResponse )
|
||||
if( msg instanceof FullHttpResponse response )
|
||||
{
|
||||
FullHttpResponse response = (FullHttpResponse) msg;
|
||||
throw new IllegalStateException( "Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString( CharsetUtil.UTF_8 ) + ')' );
|
||||
}
|
||||
|
||||
@ -76,9 +75,8 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
|
||||
websocket.environment().addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length );
|
||||
websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), converted, true );
|
||||
}
|
||||
else if( frame instanceof CloseWebSocketFrame )
|
||||
else if( frame instanceof CloseWebSocketFrame closeFrame )
|
||||
{
|
||||
CloseWebSocketFrame closeFrame = (CloseWebSocketFrame) frame;
|
||||
websocket.close( closeFrame.statusCode(), closeFrame.reasonText() );
|
||||
}
|
||||
else if( frame instanceof PingWebSocketFrame )
|
||||
|
@ -18,7 +18,7 @@ import static org.objectweb.asm.Opcodes.ICONST_0;
|
||||
|
||||
final class Reflect
|
||||
{
|
||||
static final Type OPTIONAL_IN = Optional.class.getTypeParameters()[0];
|
||||
static final java.lang.reflect.Type OPTIONAL_IN = Optional.class.getTypeParameters()[0];
|
||||
|
||||
private Reflect()
|
||||
{
|
||||
@ -52,12 +52,11 @@ final class Reflect
|
||||
{
|
||||
if( underlying instanceof Class<?> ) return (Class<?>) underlying;
|
||||
|
||||
if( underlying instanceof ParameterizedType )
|
||||
if( underlying instanceof ParameterizedType type )
|
||||
{
|
||||
ParameterizedType type = (ParameterizedType) underlying;
|
||||
if( !allowParameter )
|
||||
{
|
||||
for( Type arg : type.getActualTypeArguments() )
|
||||
for( java.lang.reflect.Type arg : type.getActualTypeArguments() )
|
||||
{
|
||||
if( arg instanceof WildcardType ) continue;
|
||||
if( arg instanceof TypeVariable && ((TypeVariable<?>) arg).getName().startsWith( "capture#" ) )
|
||||
|
@ -79,4 +79,19 @@ public class ClientTerminal implements ITerminal
|
||||
terminalChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void readDescription( NbtCompound nbt )
|
||||
{
|
||||
colour = nbt.getBoolean( "colour" );
|
||||
if( nbt.contains( "terminal" ) )
|
||||
{
|
||||
NbtCompound terminal = nbt.getCompound( "terminal" );
|
||||
resizeTerminal( terminal.getInt( "term_width" ), terminal.getInt( "term_height" ) );
|
||||
this.terminal.readFromNBT( terminal );
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteTerminal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
@ -30,6 +31,7 @@ import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
@ -70,6 +72,10 @@ public final class NetworkHandler
|
||||
registerMainThread( 12, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new );
|
||||
registerMainThread( 13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new );
|
||||
registerMainThread( 14, PlayRecordClientMessage.class, PlayRecordClientMessage::new );
|
||||
registerMainThread( 15, MonitorClientMessage.class, MonitorClientMessage::new );
|
||||
registerMainThread( 16, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new );
|
||||
registerMainThread( 17, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new );
|
||||
registerMainThread( 18, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new );
|
||||
registerMainThread( 19, UploadResultMessage.class, UploadResultMessage::new );
|
||||
}
|
||||
|
||||
@ -105,11 +111,6 @@ public final class NetworkHandler
|
||||
.getClass();
|
||||
}
|
||||
|
||||
public static void sendToPlayer( PlayerEntity player, NetworkMessage packet )
|
||||
{
|
||||
((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) );
|
||||
}
|
||||
|
||||
private static PacketByteBuf encode( NetworkMessage message )
|
||||
{
|
||||
PacketByteBuf buf = new PacketByteBuf( Unpooled.buffer() );
|
||||
@ -118,6 +119,18 @@ public final class NetworkHandler
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static void sendToPlayer( PlayerEntity player, NetworkMessage packet )
|
||||
{
|
||||
((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) );
|
||||
}
|
||||
|
||||
public static void sendToAllPlayers( NetworkMessage packet )
|
||||
{
|
||||
MinecraftServer server = GameInstanceUtils.getServer();
|
||||
server.getPlayerManager()
|
||||
.sendToAll( new CustomPayloadS2CPacket( ID, encode( packet ) ) );
|
||||
}
|
||||
|
||||
public static void sendToAllPlayers( MinecraftServer server, NetworkMessage packet )
|
||||
{
|
||||
server.getPlayerManager()
|
||||
@ -136,4 +149,14 @@ public final class NetworkHandler
|
||||
.getPlayerManager()
|
||||
.sendToAround( null, pos.x, pos.y, pos.z, range, world.getRegistryKey(), new CustomPayloadS2CPacket( ID, encode( packet ) ) );
|
||||
}
|
||||
|
||||
public static void sendToAllTracking( NetworkMessage packet, WorldChunk chunk )
|
||||
{
|
||||
// maybe bug with worlds
|
||||
for(PlayerEntity player : chunk.getWorld().getPlayers()) {
|
||||
if (player.getChunkPos().equals(chunk.getPos())) {
|
||||
((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Starts a sound on the client.
|
||||
*
|
||||
* Used by speakers to play sounds.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerMoveClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
private final Vec3d pos;
|
||||
|
||||
public SpeakerMoveClientMessage( UUID source, Vec3d pos )
|
||||
{
|
||||
this.source = source;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public SpeakerMoveClientMessage( PacketByteBuf buf )
|
||||
{
|
||||
source = buf.readUuid();
|
||||
pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketByteBuf buf )
|
||||
{
|
||||
buf.writeUuid( source );
|
||||
buf.writeDouble( pos.getX() );
|
||||
buf.writeDouble( pos.getY() );
|
||||
buf.writeDouble( pos.getZ() );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Environment( EnvType.CLIENT )
|
||||
public void handle( PacketContext context )
|
||||
{
|
||||
SoundManager.moveSound( source, pos );
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Starts a sound on the client.
|
||||
*
|
||||
* Used by speakers to play sounds.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerPlayClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
private final Vec3d pos;
|
||||
private final Identifier sound;
|
||||
private final float volume;
|
||||
private final float pitch;
|
||||
|
||||
public SpeakerPlayClientMessage( UUID source, Vec3d pos, Identifier event, float volume, float pitch )
|
||||
{
|
||||
this.source = source;
|
||||
this.pos = pos;
|
||||
sound = event;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public SpeakerPlayClientMessage( PacketByteBuf buf )
|
||||
{
|
||||
source = buf.readUuid();
|
||||
pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() );
|
||||
sound = buf.readIdentifier();
|
||||
volume = buf.readFloat();
|
||||
pitch = buf.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketByteBuf buf )
|
||||
{
|
||||
buf.writeUuid( source );
|
||||
buf.writeDouble( pos.getX() );
|
||||
buf.writeDouble( pos.getY() );
|
||||
buf.writeDouble( pos.getZ() );
|
||||
buf.writeIdentifier( sound );
|
||||
buf.writeFloat( volume );
|
||||
buf.writeFloat( pitch );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Environment( EnvType.CLIENT )
|
||||
public void handle( PacketContext context )
|
||||
{
|
||||
SoundEvent sound = new SoundEvent(this.sound);
|
||||
SoundManager.playSound( source, pos, sound, volume, pitch );
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Stops a sound on the client
|
||||
*
|
||||
* Called when a speaker is broken.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerStopClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
|
||||
public SpeakerStopClientMessage( UUID source )
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public SpeakerStopClientMessage( PacketByteBuf buf )
|
||||
{
|
||||
source = buf.readUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketByteBuf buf )
|
||||
{
|
||||
buf.writeUuid( source );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void handle( PacketContext context )
|
||||
{
|
||||
SoundManager.stopSound( source );
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ public class BlockMonitor extends BlockGeneric
|
||||
|
||||
static final EnumProperty<MonitorEdgeState> STATE = EnumProperty.of( "state", MonitorEdgeState.class );
|
||||
|
||||
public boolean advanced = false;
|
||||
public boolean advanced;
|
||||
|
||||
public BlockMonitor( Settings settings, BlockEntityType<? extends TileMonitor> type, boolean advanced )
|
||||
{
|
||||
@ -83,9 +83,8 @@ public class BlockMonitor extends BlockGeneric
|
||||
super.onPlaced( world, pos, blockState, livingEntity, itemStack );
|
||||
|
||||
BlockEntity entity = world.getBlockEntity( pos );
|
||||
if( entity instanceof TileMonitor && !world.isClient )
|
||||
if( entity instanceof TileMonitor monitor && !world.isClient )
|
||||
{
|
||||
TileMonitor monitor = (TileMonitor) entity;
|
||||
// Defer the block update if we're being placed by another TE. See #691
|
||||
if( livingEntity == null || livingEntity instanceof FakePlayer )
|
||||
{
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.monitor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
final class MonitorState
|
||||
{
|
||||
public static final MonitorState UNLOADED = new MonitorState( State.UNLOADED, null );
|
||||
public static final MonitorState MISSING = new MonitorState( State.MISSING, null );
|
||||
|
||||
private final State state;
|
||||
private final TileMonitor monitor;
|
||||
|
||||
private MonitorState( @Nonnull State state, @Nullable TileMonitor monitor )
|
||||
{
|
||||
this.state = state;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
public static MonitorState present( @Nonnull TileMonitor monitor )
|
||||
{
|
||||
return new MonitorState( State.PRESENT, monitor );
|
||||
}
|
||||
|
||||
public boolean isPresent()
|
||||
{
|
||||
return state == State.PRESENT;
|
||||
}
|
||||
|
||||
public boolean isMissing()
|
||||
{
|
||||
return state == State.MISSING;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TileMonitor getMonitor()
|
||||
{
|
||||
return monitor;
|
||||
}
|
||||
|
||||
enum State
|
||||
{
|
||||
UNLOADED,
|
||||
MISSING,
|
||||
PRESENT,
|
||||
}
|
||||
}
|
@ -51,6 +51,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
private ClientMonitor clientMonitor;
|
||||
private MonitorPeripheral peripheral;
|
||||
private boolean needsUpdate = false;
|
||||
private boolean needsValidating = false;
|
||||
private boolean destroyed = false;
|
||||
private boolean visiting = false;
|
||||
private int width = 1;
|
||||
@ -124,6 +125,13 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
@Override
|
||||
public void blockTick()
|
||||
{
|
||||
|
||||
if( needsValidating )
|
||||
{
|
||||
needsValidating = false;
|
||||
validate();
|
||||
}
|
||||
|
||||
if( needsUpdate )
|
||||
{
|
||||
needsUpdate = false;
|
||||
@ -143,7 +151,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
|
||||
if( monitor == null )
|
||||
{
|
||||
continue;
|
||||
@ -170,6 +178,8 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
|
||||
int oldXIndex = xIndex;
|
||||
int oldYIndex = yIndex;
|
||||
int oldWidth = width;
|
||||
int oldHeight = height;
|
||||
|
||||
xIndex = nbt.getInt( NBT_X );
|
||||
yIndex = nbt.getInt( NBT_Y );
|
||||
@ -180,14 +190,27 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
// If our index has changed then it's possible the origin monitor has changed. Thus
|
||||
// we'll clear our cache. If we're the origin then we'll need to remove the glList as well.
|
||||
if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null ) clientMonitor.destroy();
|
||||
if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null )
|
||||
{
|
||||
clientMonitor.destroy();
|
||||
}
|
||||
clientMonitor = null;
|
||||
}
|
||||
|
||||
if( xIndex == 0 && yIndex == 0 )
|
||||
{
|
||||
// If we're the origin terminal then create it.
|
||||
if( clientMonitor == null ) clientMonitor = new ClientMonitor( advanced, this );
|
||||
if( clientMonitor == null )
|
||||
{
|
||||
clientMonitor = new ClientMonitor( advanced, this );
|
||||
}
|
||||
clientMonitor.readDescription( nbt );
|
||||
}
|
||||
|
||||
if( oldXIndex != xIndex || oldYIndex != yIndex || oldWidth != width || oldHeight != height )
|
||||
{
|
||||
// One of our properties has changed, so ensure we redraw the block
|
||||
updateBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,9 +222,14 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
nbt.putInt( NBT_Y, yIndex );
|
||||
nbt.putInt( NBT_WIDTH, width );
|
||||
nbt.putInt( NBT_HEIGHT, height );
|
||||
|
||||
if( xIndex == 0 && yIndex == 0 && serverMonitor != null )
|
||||
{
|
||||
serverMonitor.writeDescription( nbt );
|
||||
}
|
||||
}
|
||||
|
||||
private TileMonitor getNeighbour( int x, int y )
|
||||
private MonitorState getNeighbour( int x, int y )
|
||||
{
|
||||
BlockPos pos = getPos();
|
||||
Direction right = getRight();
|
||||
@ -227,28 +255,27 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite();
|
||||
}
|
||||
|
||||
private TileMonitor getSimilarMonitorAt( BlockPos pos )
|
||||
private MonitorState getSimilarMonitorAt( BlockPos pos )
|
||||
{
|
||||
if( pos.equals( getPos() ) )
|
||||
{
|
||||
return this;
|
||||
return MonitorState.present(this);
|
||||
}
|
||||
|
||||
int y = pos.getY();
|
||||
World world = getWorld();
|
||||
if( world == null || !world.isChunkLoaded( pos ) )
|
||||
{
|
||||
return null;
|
||||
return MonitorState.UNLOADED;
|
||||
}
|
||||
|
||||
BlockEntity tile = world.getBlockEntity( pos );
|
||||
if( !(tile instanceof TileMonitor) )
|
||||
{
|
||||
return null;
|
||||
return MonitorState.MISSING;
|
||||
}
|
||||
|
||||
TileMonitor monitor = (TileMonitor) tile;
|
||||
return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() ? monitor : null;
|
||||
return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() ? MonitorState.present( monitor ) : MonitorState.MISSING;
|
||||
}
|
||||
|
||||
// region Sizing and placement stuff
|
||||
@ -302,6 +329,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
public void cancelRemoval()
|
||||
{
|
||||
super.cancelRemoval();
|
||||
needsValidating = true;
|
||||
TickScheduler.schedule( this );
|
||||
}
|
||||
|
||||
@ -329,7 +357,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return serverMonitor;
|
||||
}
|
||||
|
||||
TileMonitor origin = getOrigin();
|
||||
TileMonitor origin = getOrigin().getMonitor();
|
||||
if( origin == null )
|
||||
{
|
||||
return null;
|
||||
@ -356,7 +384,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
|
||||
if( monitor != null )
|
||||
{
|
||||
monitor.serverMonitor = serverMonitor;
|
||||
@ -450,7 +478,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return yIndex;
|
||||
}
|
||||
|
||||
private TileMonitor getOrigin()
|
||||
private MonitorState getOrigin()
|
||||
{
|
||||
return getNeighbour( 0, 0 );
|
||||
}
|
||||
@ -477,7 +505,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
|
||||
if( monitor != null && monitor.peripheral != null )
|
||||
{
|
||||
needsTerminal = true;
|
||||
@ -511,7 +539,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
|
||||
if( monitor == null )
|
||||
{
|
||||
continue;
|
||||
@ -530,7 +558,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
|
||||
private boolean mergeLeft()
|
||||
{
|
||||
TileMonitor left = getNeighbour( -1, 0 );
|
||||
TileMonitor left = getNeighbour( -1, 0 ).getMonitor();
|
||||
if( left == null || left.yIndex != 0 || left.height != height )
|
||||
{
|
||||
return false;
|
||||
@ -542,7 +570,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return false;
|
||||
}
|
||||
|
||||
TileMonitor origin = left.getOrigin();
|
||||
TileMonitor origin = left.getOrigin().getMonitor();
|
||||
if( origin != null )
|
||||
{
|
||||
origin.resize( width, height );
|
||||
@ -553,7 +581,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
|
||||
private boolean mergeRight()
|
||||
{
|
||||
TileMonitor right = getNeighbour( width, 0 );
|
||||
TileMonitor right = getNeighbour( width, 0 ).getMonitor();
|
||||
if( right == null || right.yIndex != 0 || right.height != height )
|
||||
{
|
||||
return false;
|
||||
@ -565,7 +593,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return false;
|
||||
}
|
||||
|
||||
TileMonitor origin = getOrigin();
|
||||
TileMonitor origin = getOrigin().getMonitor();
|
||||
if( origin != null )
|
||||
{
|
||||
origin.resize( width, height );
|
||||
@ -576,7 +604,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
|
||||
private boolean mergeUp()
|
||||
{
|
||||
TileMonitor above = getNeighbour( 0, height );
|
||||
TileMonitor above = getNeighbour( 0, height ).getMonitor();
|
||||
if( above == null || above.xIndex != 0 || above.width != width )
|
||||
{
|
||||
return false;
|
||||
@ -588,7 +616,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return false;
|
||||
}
|
||||
|
||||
TileMonitor origin = getOrigin();
|
||||
TileMonitor origin = getOrigin().getMonitor();
|
||||
if( origin != null )
|
||||
{
|
||||
origin.resize( width, height );
|
||||
@ -599,7 +627,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
|
||||
private boolean mergeDown()
|
||||
{
|
||||
TileMonitor below = getNeighbour( 0, -1 );
|
||||
TileMonitor below = getNeighbour( 0, -1 ).getMonitor();
|
||||
if( below == null || below.xIndex != 0 || below.width != width )
|
||||
{
|
||||
return false;
|
||||
@ -611,7 +639,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
return false;
|
||||
}
|
||||
|
||||
TileMonitor origin = below.getOrigin();
|
||||
TileMonitor origin = below.getOrigin().getMonitor();
|
||||
if( origin != null )
|
||||
{
|
||||
origin.resize( width, height );
|
||||
@ -643,7 +671,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
visiting = true;
|
||||
if( xIndex > 0 )
|
||||
{
|
||||
TileMonitor left = getNeighbour( xIndex - 1, yIndex );
|
||||
TileMonitor left = getNeighbour( xIndex - 1, yIndex ).getMonitor();
|
||||
if( left != null )
|
||||
{
|
||||
left.contract();
|
||||
@ -651,7 +679,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
}
|
||||
if( xIndex + 1 < width )
|
||||
{
|
||||
TileMonitor right = getNeighbour( xIndex + 1, yIndex );
|
||||
TileMonitor right = getNeighbour( xIndex + 1, yIndex ).getMonitor();
|
||||
if( right != null )
|
||||
{
|
||||
right.contract();
|
||||
@ -659,7 +687,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
}
|
||||
if( yIndex > 0 )
|
||||
{
|
||||
TileMonitor below = getNeighbour( xIndex, yIndex - 1 );
|
||||
TileMonitor below = getNeighbour( xIndex, yIndex - 1 ).getMonitor();
|
||||
if( below != null )
|
||||
{
|
||||
below.contract();
|
||||
@ -667,7 +695,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
}
|
||||
if( yIndex + 1 < height )
|
||||
{
|
||||
TileMonitor above = getNeighbour( xIndex, yIndex + 1 );
|
||||
TileMonitor above = getNeighbour( xIndex, yIndex + 1 ).getMonitor();
|
||||
if( above != null )
|
||||
{
|
||||
above.contract();
|
||||
@ -681,11 +709,11 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
int height = this.height;
|
||||
int width = this.width;
|
||||
|
||||
TileMonitor origin = getOrigin();
|
||||
TileMonitor origin = getOrigin().getMonitor();
|
||||
if( origin == null )
|
||||
{
|
||||
TileMonitor right = width > 1 ? getNeighbour( 1, 0 ) : null;
|
||||
TileMonitor below = height > 1 ? getNeighbour( 0, 1 ) : null;
|
||||
TileMonitor right = width > 1 ? getNeighbour( 1, 0 ).getMonitor() : null;
|
||||
TileMonitor below = height > 1 ? getNeighbour( 0, 1 ).getMonitor() : null;
|
||||
|
||||
if( right != null )
|
||||
{
|
||||
@ -711,7 +739,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
TileMonitor monitor = origin.getNeighbour( x, y );
|
||||
TileMonitor monitor = origin.getNeighbour( x, y ).getMonitor();
|
||||
if( monitor != null )
|
||||
{
|
||||
continue;
|
||||
@ -730,17 +758,17 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
}
|
||||
if( x > 0 )
|
||||
{
|
||||
left = origin.getNeighbour( 0, y );
|
||||
left = origin.getNeighbour( 0, y ).getMonitor();
|
||||
left.resize( x, 1 );
|
||||
}
|
||||
if( x + 1 < width )
|
||||
{
|
||||
right = origin.getNeighbour( x + 1, y );
|
||||
right = origin.getNeighbour( x + 1, y ).getMonitor();
|
||||
right.resize( width - (x + 1), 1 );
|
||||
}
|
||||
if( y + 1 < height )
|
||||
{
|
||||
below = origin.getNeighbour( 0, y + 1 );
|
||||
below = origin.getNeighbour( 0, y + 1 ).getMonitor();
|
||||
below.resize( width, height - (y + 1) );
|
||||
}
|
||||
|
||||
@ -767,6 +795,34 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
}
|
||||
// endregion
|
||||
|
||||
private boolean checkMonitorAt( int xIndex, int yIndex )
|
||||
{
|
||||
MonitorState state = getNeighbour( xIndex, yIndex );
|
||||
if( state.isMissing() ) return false;
|
||||
|
||||
TileMonitor monitor = state.getMonitor();
|
||||
if( monitor == null ) return true;
|
||||
|
||||
return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height;
|
||||
}
|
||||
|
||||
private void validate()
|
||||
{
|
||||
if( xIndex == 0 && yIndex == 0 && width == 1 || height == 1 ) return;
|
||||
|
||||
if( checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) &&
|
||||
checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves
|
||||
// later.
|
||||
ComputerCraft.log.warn( "Monitor is malformed, resetting to 1x1." );
|
||||
resize( 1, 1 );
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
private void monitorTouched( float xPos, float yPos, float zPos )
|
||||
{
|
||||
XYPair pair = XYPair.of( xPos, yPos, zPos, getDirection(), getOrientation() )
|
||||
@ -799,7 +855,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
|
||||
if( monitor == null )
|
||||
{
|
||||
continue;
|
||||
|
@ -12,17 +12,23 @@ import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.fabric.mixin.SoundEventAccess;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||
import net.minecraft.block.enums.Instrument;
|
||||
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
@ -34,16 +40,40 @@ import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
*/
|
||||
public abstract class SpeakerPeripheral implements IPeripheral
|
||||
{
|
||||
private static final int MIN_TICKS_BETWEEN_SOUNDS = 1;
|
||||
|
||||
private final AtomicInteger notesThisTick = new AtomicInteger();
|
||||
private long clock = 0;
|
||||
private long lastPlayTime = 0;
|
||||
|
||||
private long lastPositionTime;
|
||||
private Vec3d lastPosition;
|
||||
|
||||
public void update()
|
||||
{
|
||||
clock++;
|
||||
notesThisTick.set( 0 );
|
||||
|
||||
// Push position updates to any speakers which have ever played a note,
|
||||
// have moved by a non-trivial amount and haven't had a position update
|
||||
// in the last second.
|
||||
if( lastPlayTime > 0 && (clock - lastPositionTime) >= 20 )
|
||||
{
|
||||
Vec3d position = getPosition();
|
||||
if( lastPosition == null || lastPosition.distanceTo( position ) >= 0.1 )
|
||||
{
|
||||
lastPosition = position;
|
||||
lastPositionTime = clock;
|
||||
NetworkHandler.sendToAllTracking(
|
||||
new SpeakerMoveClientMessage( getSource(), position ),
|
||||
getWorld().getWorldChunk( new BlockPos( position ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract UUID getSource();
|
||||
|
||||
public boolean madeSound( long ticks )
|
||||
{
|
||||
return clock - lastPlayTime <= ticks;
|
||||
@ -90,16 +120,20 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
|
||||
private synchronized boolean playSound( ILuaContext context, Identifier name, float volume, float pitch, boolean isNote ) throws LuaException
|
||||
{
|
||||
if( clock - lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS && (!isNote || clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick) )
|
||||
if( clock - lastPlayTime < MIN_TICKS_BETWEEN_SOUNDS )
|
||||
{
|
||||
// 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;
|
||||
// Rate limiting occurs when we've already played a sound within the last tick.
|
||||
if( !isNote ) return false;
|
||||
// Or we've played more notes than allowable within the current tick.
|
||||
if( clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick ) return false;
|
||||
}
|
||||
|
||||
World world = getWorld();
|
||||
Vec3d pos = getPosition();
|
||||
|
||||
float actualVolume = MathHelper.clamp( volume, 0.0f, 3.0f );
|
||||
float range = actualVolume * 16;
|
||||
|
||||
context.issueMainThreadTask( () -> {
|
||||
MinecraftServer server = world.getServer();
|
||||
if( server == null )
|
||||
@ -107,15 +141,18 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
return null;
|
||||
}
|
||||
|
||||
float adjVolume = Math.min( volume, 3.0f );
|
||||
server.getPlayerManager()
|
||||
.sendToAround( null,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
adjVolume > 1.0f ? 16 * adjVolume : 16.0,
|
||||
world.getRegistryKey(),
|
||||
new PlaySoundIdS2CPacket( name, SoundCategory.RECORDS, pos, adjVolume, pitch ) );
|
||||
if( isNote )
|
||||
{
|
||||
server.getPlayerManager().sendToAround(
|
||||
null, pos.x, pos.y, pos.z, range, world.getRegistryKey(),
|
||||
new PlaySoundIdS2CPacket( name, SoundCategory.RECORDS, pos, actualVolume, pitch )
|
||||
);
|
||||
} else {
|
||||
NetworkHandler.sendToAllAround(
|
||||
new SpeakerPlayClientMessage( getSource(), pos, name, actualVolume, pitch ),
|
||||
world, pos, range
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} );
|
||||
|
||||
|
@ -19,12 +19,14 @@ import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TileSpeaker extends TileGeneric implements IPeripheralTile
|
||||
{
|
||||
public static final int MIN_TICKS_BETWEEN_SOUNDS = 1;
|
||||
|
||||
private final SpeakerPeripheral peripheral;
|
||||
private final UUID source = UUID.randomUUID();
|
||||
|
||||
public TileSpeaker( BlockEntityType<TileSpeaker> type, BlockPos pos, BlockState state )
|
||||
{
|
||||
@ -66,6 +68,12 @@ public class TileSpeaker extends TileGeneric implements IPeripheralTile
|
||||
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getSource()
|
||||
{
|
||||
return speaker.source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( @Nullable IPeripheral other )
|
||||
{
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A speaker peripheral which is used on an upgrade, and so is only attached to one computer.
|
||||
*/
|
||||
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral
|
||||
{
|
||||
private final UUID source = UUID.randomUUID();
|
||||
|
||||
@Override
|
||||
protected final UUID getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach( @Nonnull IComputerAccess computer )
|
||||
{
|
||||
NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) );
|
||||
}
|
||||
}
|
@ -8,10 +8,11 @@ package dan200.computercraft.shared.pocket.peripherals;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class PocketSpeakerPeripheral extends SpeakerPeripheral
|
||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral
|
||||
{
|
||||
private World world = null;
|
||||
private Vec3d position = Vec3d.ZERO;
|
||||
|
@ -13,6 +13,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeType;
|
||||
import dan200.computercraft.shared.ComputerCraftRegistry;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
@ -71,7 +72,7 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade
|
||||
}
|
||||
}
|
||||
|
||||
private static class Peripheral extends SpeakerPeripheral
|
||||
private static class Peripheral extends UpgradeSpeakerPeripheral
|
||||
{
|
||||
ITurtleAccess turtle;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user