mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-13 03:30:29 +00:00
Make monitors non-ticking
- Convert terminals from a polling-based system to a more event-driven one: they now accept an onChanged callback, which marks the parent as dirty. - Schedule ticks when monitors are marked as dirty. - Add several missing @Overrides. This has nothing to do with the rest of the changes, but I'm bad at good git practice.
This commit is contained in:
parent
8a7e651c99
commit
83b01d35eb
@ -16,6 +16,7 @@ public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
|
||||
{
|
||||
void advance( double v );
|
||||
|
||||
@Override
|
||||
default void update()
|
||||
{
|
||||
advance( 0.05 );
|
||||
|
@ -26,12 +26,14 @@ public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T>
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void shutdown()
|
||||
{
|
||||
super.shutdown();
|
||||
pending.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean queue( Supplier<T> resource )
|
||||
{
|
||||
if( !active ) return false;
|
||||
@ -40,6 +42,7 @@ public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T>
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void release( T resource )
|
||||
{
|
||||
super.release( resource );
|
||||
|
@ -239,6 +239,7 @@ public class HttpRequest extends Resource<HttpRequest>
|
||||
if( tryClose() ) environment.queueEvent( SUCCESS_EVENT, new Object[] { address, object } );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
@ -29,11 +29,18 @@ public class Terminal
|
||||
private final Palette m_palette;
|
||||
|
||||
private boolean m_changed;
|
||||
private final Runnable onChanged;
|
||||
|
||||
public Terminal( int width, int height )
|
||||
{
|
||||
this( width, height, null );
|
||||
}
|
||||
|
||||
public Terminal( int width, int height, Runnable changedCallback )
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
this.onChanged = changedCallback;
|
||||
|
||||
m_cursorColour = 0;
|
||||
m_cursorBackgroundColour = 15;
|
||||
@ -65,7 +72,7 @@ public class Terminal
|
||||
m_cursorY = 0;
|
||||
m_cursorBlink = false;
|
||||
clear();
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
m_palette.resetColours();
|
||||
}
|
||||
|
||||
@ -122,7 +129,7 @@ public class Terminal
|
||||
m_backgroundColour[i].write( oldBackgroundColour[i] );
|
||||
}
|
||||
}
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public void setCursorPos( int x, int y )
|
||||
@ -131,7 +138,7 @@ public class Terminal
|
||||
{
|
||||
m_cursorX = x;
|
||||
m_cursorY = y;
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +147,7 @@ public class Terminal
|
||||
if( m_cursorBlink != blink )
|
||||
{
|
||||
m_cursorBlink = blink;
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +156,7 @@ public class Terminal
|
||||
if( m_cursorColour != colour )
|
||||
{
|
||||
m_cursorColour = colour;
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +165,7 @@ public class Terminal
|
||||
if( m_cursorBackgroundColour != colour )
|
||||
{
|
||||
m_cursorBackgroundColour = colour;
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +208,7 @@ public class Terminal
|
||||
m_text[y].write( text, x );
|
||||
m_textColour[y].write( textColour, x );
|
||||
m_backgroundColour[y].write( backgroundColour, x );
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +221,7 @@ public class Terminal
|
||||
m_text[y].write( text, x );
|
||||
m_textColour[y].fill( base16.charAt( m_cursorColour ), x, x + text.length() );
|
||||
m_backgroundColour[y].fill( base16.charAt( m_cursorBackgroundColour ), x, x + text.length() );
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +251,7 @@ public class Terminal
|
||||
m_text = newText;
|
||||
m_textColour = newTextColour;
|
||||
m_backgroundColour = newBackgroundColour;
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +263,7 @@ public class Terminal
|
||||
m_textColour[y].fill( base16.charAt( m_cursorColour ) );
|
||||
m_backgroundColour[y].fill( base16.charAt( m_cursorBackgroundColour ) );
|
||||
}
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public synchronized void clearLine()
|
||||
@ -267,7 +274,7 @@ public class Terminal
|
||||
m_text[y].fill( ' ' );
|
||||
m_textColour[y].fill( base16.charAt( m_cursorColour ) );
|
||||
m_backgroundColour[y].fill( base16.charAt( m_cursorBackgroundColour ) );
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +292,7 @@ public class Terminal
|
||||
m_text[y].write( text );
|
||||
m_textColour[y].write( textColour );
|
||||
m_backgroundColour[y].write( backgroundColour );
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public synchronized TextBuffer getTextColourLine( int y )
|
||||
@ -306,17 +313,23 @@ public class Terminal
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getChanged()
|
||||
/**
|
||||
* @deprecated All {@code *Changed()} methods are deprecated: one should pass in a callback
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final boolean getChanged()
|
||||
{
|
||||
return m_changed;
|
||||
}
|
||||
|
||||
public void setChanged()
|
||||
public final void setChanged()
|
||||
{
|
||||
m_changed = true;
|
||||
if( onChanged != null ) onChanged.run();
|
||||
}
|
||||
|
||||
public void clearChanged()
|
||||
public final void clearChanged()
|
||||
{
|
||||
m_changed = false;
|
||||
}
|
||||
@ -371,6 +384,6 @@ public class Terminal
|
||||
{
|
||||
m_palette.readFromNBT( nbt );
|
||||
}
|
||||
m_changed = true;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,14 @@ public class ClientTerminal implements ITerminal
|
||||
m_terminalChanged = false;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
if( m_terminal != null )
|
||||
{
|
||||
m_terminalChanged |= m_terminal.getChanged();
|
||||
m_terminal.clearChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pollTerminalChanged()
|
||||
{
|
||||
boolean changed = m_terminalChanged;
|
||||
m_terminalChanged = false;
|
||||
|
||||
Terminal terminal = m_terminal;
|
||||
if( terminal != null ) terminal.clearChanged();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -71,7 +66,7 @@ public class ClientTerminal implements ITerminal
|
||||
{
|
||||
if( m_terminal == null )
|
||||
{
|
||||
m_terminal = new Terminal( width, height );
|
||||
m_terminal = new Terminal( width, height, () -> m_terminalChanged = true );
|
||||
m_terminalChanged = true;
|
||||
}
|
||||
else
|
||||
|
@ -9,35 +9,33 @@ package dan200.computercraft.shared.common;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ServerTerminal implements ITerminal
|
||||
{
|
||||
private final boolean m_colour;
|
||||
private Terminal m_terminal;
|
||||
private boolean m_terminalChanged;
|
||||
private boolean m_terminalChangedLastFrame;
|
||||
private final AtomicBoolean m_terminalChanged = new AtomicBoolean( false );
|
||||
private boolean m_terminalChangedLastFrame = false;
|
||||
|
||||
public ServerTerminal( boolean colour )
|
||||
{
|
||||
m_colour = colour;
|
||||
m_terminal = null;
|
||||
m_terminalChanged = false;
|
||||
m_terminalChangedLastFrame = false;
|
||||
}
|
||||
|
||||
public ServerTerminal( boolean colour, int terminalWidth, int terminalHeight )
|
||||
{
|
||||
m_colour = colour;
|
||||
m_terminal = new Terminal( terminalWidth, terminalHeight );
|
||||
m_terminalChanged = false;
|
||||
m_terminalChangedLastFrame = false;
|
||||
m_terminal = new Terminal( terminalWidth, terminalHeight, this::markTerminalChanged );
|
||||
}
|
||||
|
||||
public void resize( int width, int height )
|
||||
protected void resize( int width, int height )
|
||||
{
|
||||
if( m_terminal == null )
|
||||
{
|
||||
m_terminal = new Terminal( width, height );
|
||||
m_terminalChanged = true;
|
||||
m_terminal = new Terminal( width, height, this::markTerminalChanged );
|
||||
markTerminalChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -50,23 +48,21 @@ public class ServerTerminal implements ITerminal
|
||||
if( m_terminal != null )
|
||||
{
|
||||
m_terminal = null;
|
||||
m_terminalChanged = true;
|
||||
markTerminalChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void markTerminalChanged()
|
||||
{
|
||||
m_terminalChanged = true;
|
||||
m_terminalChanged.set( true );
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
m_terminalChangedLastFrame = m_terminalChanged || (m_terminal != null && m_terminal.getChanged());
|
||||
if( m_terminal != null )
|
||||
{
|
||||
m_terminal.clearChanged();
|
||||
}
|
||||
m_terminalChanged = false;
|
||||
Terminal terminal = m_terminal;
|
||||
if( terminal != null ) terminal.clearChanged();
|
||||
|
||||
m_terminalChangedLastFrame = m_terminalChanged.getAndSet( false );
|
||||
}
|
||||
|
||||
public boolean hasTerminalChanged()
|
||||
|
@ -32,10 +32,8 @@ public class ClientComputer extends ClientTerminal implements IComputer
|
||||
m_instanceID = instanceID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
super.update();
|
||||
m_changedLastFrame = m_changed;
|
||||
m_changed = false;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
package dan200.computercraft.shared.peripheral.common;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.common.BlockDirectional;
|
||||
import dan200.computercraft.shared.common.BlockGeneric;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
@ -39,7 +39,7 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BlockPeripheral extends BlockDirectional
|
||||
public class BlockPeripheral extends BlockGeneric
|
||||
{
|
||||
public static class Properties
|
||||
{
|
||||
@ -466,11 +466,12 @@ public class BlockPeripheral extends BlockDirectional
|
||||
case DiskDrive:
|
||||
case Printer:
|
||||
{
|
||||
if( stack.hasDisplayName() && tile instanceof ITilePeripheral )
|
||||
EnumFacing dir = DirectionUtil.fromEntityRot( player );
|
||||
if( stack.hasDisplayName() && tile instanceof TilePeripheralBase )
|
||||
{
|
||||
ITilePeripheral peripheral = (ITilePeripheral) tile;
|
||||
TilePeripheralBase peripheral = (TilePeripheralBase) tile;
|
||||
peripheral.setLabel( stack.getDisplayName() );
|
||||
peripheral.setDirection( DirectionUtil.fromEntityRot( player ) );
|
||||
peripheral.setDirection( dir );
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -558,14 +559,18 @@ public class BlockPeripheral extends BlockDirectional
|
||||
@Override
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public AxisAlignedBB getBoundingBox( IBlockState state, IBlockAccess source, BlockPos pos )
|
||||
public AxisAlignedBB getBoundingBox( IBlockState state, IBlockAccess world, BlockPos pos )
|
||||
{
|
||||
if( getPeripheralType( state ) == PeripheralType.WirelessModem )
|
||||
{
|
||||
return ModemBounds.getBounds( getDirection( source, pos ) );
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile instanceof TileWirelessModem )
|
||||
{
|
||||
return ModemBounds.getBounds( ((TileWirelessModem) tile).getDirection() );
|
||||
}
|
||||
}
|
||||
|
||||
return super.getBoundingBox( state, source, pos );
|
||||
return super.getBoundingBox( state, world, pos );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,13 +6,12 @@
|
||||
|
||||
package dan200.computercraft.shared.peripheral.common;
|
||||
|
||||
import dan200.computercraft.shared.common.IDirectionalTile;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
|
||||
/**
|
||||
* The tile for {@link BlockPeripheral}.
|
||||
*/
|
||||
public interface ITilePeripheral extends IDirectionalTile
|
||||
public interface ITilePeripheral
|
||||
{
|
||||
PeripheralType getPeripheralType();
|
||||
|
||||
|
@ -73,6 +73,7 @@ public abstract class TilePeripheralBase extends TileGeneric implements IPeriphe
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel( String label )
|
||||
{
|
||||
m_label = label;
|
||||
|
@ -8,12 +8,16 @@ package dan200.computercraft.shared.peripheral.monitor;
|
||||
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.common.ServerTerminal;
|
||||
import dan200.computercraft.shared.util.TickScheduler;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ServerMonitor extends ServerTerminal
|
||||
{
|
||||
private final TileMonitor origin;
|
||||
private int textScale = 2;
|
||||
private boolean resized;
|
||||
private final AtomicBoolean resized = new AtomicBoolean( false );
|
||||
private final AtomicBoolean changed = new AtomicBoolean( false );
|
||||
|
||||
public ServerMonitor( boolean colour, TileMonitor origin )
|
||||
{
|
||||
@ -41,10 +45,28 @@ public class ServerMonitor extends ServerTerminal
|
||||
if( oldWidth != termWidth || oldHeight != termHeight )
|
||||
{
|
||||
getTerminal().clear();
|
||||
resized = true;
|
||||
resized.set( true );
|
||||
markChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markTerminalChanged()
|
||||
{
|
||||
super.markTerminalChanged();
|
||||
markChanged();
|
||||
}
|
||||
|
||||
private void markChanged()
|
||||
{
|
||||
if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin );
|
||||
}
|
||||
|
||||
protected void clearChanged()
|
||||
{
|
||||
changed.set( false );
|
||||
}
|
||||
|
||||
public int getTextScale()
|
||||
{
|
||||
return textScale;
|
||||
@ -57,14 +79,14 @@ public class ServerMonitor extends ServerTerminal
|
||||
rebuild();
|
||||
}
|
||||
|
||||
public synchronized boolean pollResized()
|
||||
public boolean pollResized()
|
||||
{
|
||||
if( resized )
|
||||
{
|
||||
resized = false;
|
||||
return true;
|
||||
}
|
||||
return resized.getAndSet( false );
|
||||
}
|
||||
|
||||
return false;
|
||||
public boolean pollTerminalChanged()
|
||||
{
|
||||
update();
|
||||
return hasTerminalChanged();
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,20 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.common.ServerTerminal;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.common.TilePeripheralBase;
|
||||
import dan200.computercraft.shared.peripheral.common.IPeripheralTile;
|
||||
import dan200.computercraft.shared.peripheral.common.ITilePeripheral;
|
||||
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
@ -28,7 +33,7 @@ import javax.annotation.Nonnull;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TileMonitor extends TilePeripheralBase
|
||||
public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeripheralTile
|
||||
{
|
||||
// Statics
|
||||
|
||||
@ -78,6 +83,7 @@ public class TileMonitor extends TilePeripheralBase
|
||||
super.onLoad();
|
||||
m_advanced = getBlockState().getValue( BlockPeripheral.Properties.VARIANT )
|
||||
.getPeripheralType() == PeripheralType.AdvancedMonitor;
|
||||
world.scheduleUpdate( getPos(), getBlockType(), 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,44 +152,32 @@ public class TileMonitor extends TilePeripheralBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
public void updateTick()
|
||||
{
|
||||
super.update();
|
||||
if( m_xIndex != 0 || m_yIndex != 0 || m_serverMonitor == null ) return;
|
||||
|
||||
if( !getWorld().isRemote )
|
||||
m_serverMonitor.clearChanged();
|
||||
|
||||
if( m_serverMonitor.pollResized() )
|
||||
{
|
||||
if( m_xIndex == 0 && m_yIndex == 0 && m_serverMonitor != null )
|
||||
for( int x = 0; x < m_width; x++ )
|
||||
{
|
||||
if( m_serverMonitor.pollResized() )
|
||||
for( int y = 0; y < m_height; y++ )
|
||||
{
|
||||
for( int x = 0; x < m_width; x++ )
|
||||
{
|
||||
for( int y = 0; y < m_height; y++ )
|
||||
{
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
if( monitor == null ) continue;
|
||||
TileMonitor monitor = getNeighbour( x, y );
|
||||
if( monitor == null ) continue;
|
||||
|
||||
for( IComputerAccess computer : monitor.m_computers )
|
||||
{
|
||||
computer.queueEvent( "monitor_resize", new Object[] {
|
||||
computer.getAttachmentName()
|
||||
} );
|
||||
}
|
||||
}
|
||||
for( IComputerAccess computer : monitor.m_computers )
|
||||
{
|
||||
computer.queueEvent( "monitor_resize", new Object[] {
|
||||
computer.getAttachmentName()
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_serverMonitor.update();
|
||||
if( m_serverMonitor.hasTerminalChanged() ) updateBlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_xIndex == 0 && m_yIndex == 0 && m_clientMonitor != null )
|
||||
{
|
||||
m_clientMonitor.update();
|
||||
}
|
||||
}
|
||||
if( m_serverMonitor.pollTerminalChanged() ) updateBlock();
|
||||
}
|
||||
|
||||
// IPeripheralTile implementation
|
||||
@ -197,6 +191,12 @@ public class TileMonitor extends TilePeripheralBase
|
||||
return m_peripheral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PeripheralType getPeripheralType()
|
||||
{
|
||||
return m_advanced ? PeripheralType.AdvancedMonitor : PeripheralType.Monitor;
|
||||
}
|
||||
|
||||
public ServerMonitor getCachedServerMonitor()
|
||||
{
|
||||
return m_serverMonitor;
|
||||
@ -319,7 +319,6 @@ public class TileMonitor extends TilePeripheralBase
|
||||
}
|
||||
// Sizing and placement stuff
|
||||
|
||||
@Override
|
||||
public EnumFacing getDirection()
|
||||
{
|
||||
int dir = getDir() % 6;
|
||||
@ -444,7 +443,7 @@ public class TileMonitor extends TilePeripheralBase
|
||||
return getSimilarMonitorAt( pos.offset( right, xOffset ).offset( down, yOffset ) );
|
||||
}
|
||||
|
||||
public TileMonitor getOrigin()
|
||||
private TileMonitor getOrigin()
|
||||
{
|
||||
return getNeighbour( 0, 0 );
|
||||
}
|
||||
@ -752,15 +751,16 @@ public class TileMonitor extends TilePeripheralBase
|
||||
{
|
||||
case Monitor:
|
||||
case AdvancedMonitor:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative )
|
||||
{
|
||||
if( !creative ) drops.add( PeripheralItemFactory.create( this ) );
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +177,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastState( boolean force )
|
||||
{
|
||||
super.broadcastState( force );
|
||||
|
Loading…
Reference in New Issue
Block a user