1
0
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:
Nikita Savyolov 2021-10-03 22:54:13 +03:00
parent d4f1e34023
commit 861a9e199d
No known key found for this signature in database
GPG Key ID: 32C1EF023AFC184B
24 changed files with 631 additions and 84 deletions

View File

@ -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;

View 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()
{
}
}
}

View File

@ -62,6 +62,7 @@ public interface IAPIEnvironment
@Nullable
IPeripheral getPeripheral( ComputerSide side );
@Nullable
String getLabel();
void setLabel( @Nullable String label );

View File

@ -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() );

View File

@ -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.
*

View File

@ -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() ) );

View File

@ -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.

View File

@ -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 )
);
}

View File

@ -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 )
);
}
}

View File

@ -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 )

View File

@ -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#" ) )

View File

@ -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();
}
}
}

View File

@ -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 ) ) );
}
}
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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 )
{

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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;
} );

View File

@ -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 )
{

View File

@ -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 ) );
}
}

View File

@ -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;

View File

@ -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;