Added Computer interface upgrades
This commit is contained in:
parent
f33c1d3a54
commit
0c1a0bfdd0
20 changed files with 169 additions and 31 deletions
|
@ -2,6 +2,9 @@ package cr0s.warpdrive.api.computer;
|
|||
|
||||
public interface IInterfaced {
|
||||
|
||||
// return true if it supports the interface
|
||||
boolean isInterfaceEnabled();
|
||||
|
||||
// return true if it supports the interface
|
||||
Object[] isInterfaced();
|
||||
|
||||
|
|
|
@ -42,6 +42,6 @@ public abstract class TileEntityAbstractEnergyCoreOrController extends TileEntit
|
|||
getClass().getSimpleName(),
|
||||
name,
|
||||
Commons.format(world, pos),
|
||||
connectedComputers == null ? "~NULL~" : connectedComputers );
|
||||
CC_connectedComputers == null ? "~NULL~" : CC_connectedComputers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import cr0s.warpdrive.WarpDrive;
|
|||
import cr0s.warpdrive.api.FunctionGet;
|
||||
import cr0s.warpdrive.api.FunctionSetVector;
|
||||
import cr0s.warpdrive.config.WarpDriveConfig;
|
||||
import cr0s.warpdrive.data.EnumComponentType;
|
||||
import cr0s.warpdrive.data.Vector3;
|
||||
import cr0s.warpdrive.data.VectorI;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
@ -48,6 +49,7 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
// Common computer properties
|
||||
protected String peripheralName = null;
|
||||
private String[] methodsArray = {};
|
||||
private boolean isAlwaysInterfaced = true;
|
||||
|
||||
// String returned to LUA script in case of error
|
||||
public static final String COMPUTER_ERROR_TAG = "!ERROR!";
|
||||
|
@ -59,12 +61,11 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
protected volatile List<String> CC_scripts = null;
|
||||
|
||||
// OpenComputer specific properties
|
||||
protected boolean OC_enable = true;
|
||||
protected Node OC_node = null;
|
||||
protected boolean OC_addedToNetwork = false;
|
||||
|
||||
// ComputerCraft specific properties
|
||||
protected final HashMap<Integer, IComputerAccess> connectedComputers = new HashMap<>();
|
||||
protected final HashMap<Integer, IComputerAccess> CC_connectedComputers = new HashMap<>();
|
||||
|
||||
public TileEntityAbstractInterfaced() {
|
||||
super();
|
||||
|
@ -79,6 +80,46 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
}
|
||||
|
||||
// WarpDrive abstraction layer
|
||||
protected void doRequireUpgradeToInterface() {
|
||||
assert isAlwaysInterfaced;
|
||||
|
||||
isAlwaysInterfaced = false;
|
||||
setUpgradeMaxCount(EnumComponentType.COMPUTER_INTERFACE, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mountUpgrade(final Object upgrade) {
|
||||
final boolean isSuccess = super.mountUpgrade(upgrade);
|
||||
if (isSuccess) {
|
||||
if (WarpDriveConfig.isComputerCraftLoaded) {
|
||||
CC_mount();
|
||||
}
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
OC_constructor();
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dismountUpgrade(final Object upgrade) {
|
||||
final boolean isSuccess = super.dismountUpgrade(upgrade);
|
||||
if (isSuccess) {
|
||||
if (WarpDriveConfig.isComputerCraftLoaded) {
|
||||
CC_unmount();
|
||||
}
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
OC_destructor();
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterfaceEnabled() {
|
||||
return isAlwaysInterfaced || getUpgradeCount(EnumComponentType.COMPUTER_INTERFACE) > 0;
|
||||
}
|
||||
|
||||
protected void addMethods(final String[] methodsToAdd) {
|
||||
if (methodsArray == null) {
|
||||
methodsArray = methodsToAdd;
|
||||
|
@ -97,12 +138,13 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
return (url != null);
|
||||
}
|
||||
|
||||
// TileEntity overrides
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
if (!OC_addedToNetwork && OC_enable) {
|
||||
if (!OC_addedToNetwork && isInterfaceEnabled()) {
|
||||
OC_addedToNetwork = true;
|
||||
Network.joinOrCreateNetwork(this);
|
||||
}
|
||||
|
@ -117,7 +159,7 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
}
|
||||
|
||||
// deferred constructor so the derived class can finish it's initialization first
|
||||
if (WarpDriveConfig.isOpenComputersLoaded && OC_node == null) {
|
||||
if (WarpDriveConfig.isOpenComputersLoaded && OC_node == null && isInterfaceEnabled()) {
|
||||
OC_constructor();
|
||||
}
|
||||
super.validate();
|
||||
|
@ -126,10 +168,7 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
@Override
|
||||
public void invalidate() {
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
if (OC_node != null) {
|
||||
OC_node.remove();
|
||||
OC_node = null;
|
||||
}
|
||||
OC_destructor();
|
||||
}
|
||||
super.invalidate();
|
||||
}
|
||||
|
@ -137,10 +176,7 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
@Override
|
||||
public void onChunkUnload() {
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
if (OC_node != null) {
|
||||
OC_node.remove();
|
||||
OC_node = null;
|
||||
}
|
||||
OC_destructor();
|
||||
}
|
||||
super.onChunkUnload();
|
||||
}
|
||||
|
@ -149,7 +185,8 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
public void readFromNBT(final NBTTagCompound tagCompound) {
|
||||
super.readFromNBT(tagCompound);
|
||||
if ( WarpDriveConfig.isOpenComputersLoaded
|
||||
&& FMLCommonHandler.instance().getEffectiveSide().isServer() ) {
|
||||
&& FMLCommonHandler.instance().getEffectiveSide().isServer()
|
||||
&& isInterfaceEnabled() ) {
|
||||
if (OC_node == null) {
|
||||
OC_constructor();
|
||||
}
|
||||
|
@ -200,7 +237,11 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
return (((((super.hashCode() + (world == null ? 0 : world.provider.getDimension()) << 4) + pos.getX()) << 4) + pos.getY()) << 4) + pos.getZ();
|
||||
}
|
||||
|
||||
// Dirty cheap conversion methods
|
||||
// Interface proxies are used to
|
||||
// - convert arguments,
|
||||
// - log LUA calls,
|
||||
// - block connection when missing the Computer interface upgrade
|
||||
// note: direct API calls remains possible without upgrade, as it's lore dependant
|
||||
@Optional.Method(modid = "opencomputers")
|
||||
protected Object[] OC_convertArgumentsAndLogCall(final Context context, final Arguments args) {
|
||||
final Object[] arguments = new Object[args.count()];
|
||||
|
@ -219,6 +260,9 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
Commons.format(world, pos),
|
||||
peripheralName, context, methodName, Commons.format(arguments)));
|
||||
}
|
||||
if (!isInterfaceEnabled()) {
|
||||
throw new RuntimeException("Missing Computer interface upgrade.");
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
@ -230,13 +274,20 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
Commons.format(world, pos),
|
||||
peripheralName, methodName, Commons.format(arguments)));
|
||||
}
|
||||
if (!isInterfaceEnabled() && !methodName.equals("isInterfaced")) {
|
||||
throw new RuntimeException("Missing Computer interface upgrade.");
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
|
||||
// Common OC/CC methods
|
||||
@Override
|
||||
public Object[] isInterfaced() {
|
||||
return new Object[] { true, "I'm a WarpDrive computer interfaced tile entity." };
|
||||
if (isInterfaceEnabled()) {
|
||||
return new Object[] { true, "I'm a WarpDrive computer interfaced tile entity." };
|
||||
} else {
|
||||
return new Object[] { false, "Missing Computer interface upgrade." };
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -377,9 +428,23 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
|
||||
@Override
|
||||
@Optional.Method(modid = "computercraft")
|
||||
public void attach(@Nonnull final IComputerAccess computer) {
|
||||
final int id = computer.getID();
|
||||
connectedComputers.put(id, computer);
|
||||
public void attach(@Nonnull final IComputerAccess computerAccess) {
|
||||
final int id = computerAccess.getID();
|
||||
CC_connectedComputers.put(id, computerAccess);
|
||||
if (isInterfaceEnabled()) {
|
||||
CC_mount(computerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
@Optional.Method(modid = "computercraft")
|
||||
private void CC_mount() {
|
||||
for (final IComputerAccess computerAccess : CC_connectedComputers.values()) {
|
||||
CC_mount(computerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
@Optional.Method(modid = "computercraft")
|
||||
private void CC_mount(@Nonnull final IComputerAccess computer) {
|
||||
if (CC_hasResource && WarpDriveConfig.G_LUA_SCRIPTS != WarpDriveConfig.LUA_SCRIPTS_NONE) {
|
||||
try {
|
||||
final String modid = WarpDrive.MODID.toLowerCase();
|
||||
|
@ -402,11 +467,45 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
}
|
||||
}
|
||||
|
||||
@Optional.Method(modid = "computercraft")
|
||||
private void CC_unmount() {
|
||||
for (final IComputerAccess computerAccess : CC_connectedComputers.values()) {
|
||||
CC_unmount(computerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
@Optional.Method(modid = "computercraft")
|
||||
private void CC_unmount(@Nonnull final IComputerAccess computer) {
|
||||
if (CC_hasResource && WarpDriveConfig.G_LUA_SCRIPTS != WarpDriveConfig.LUA_SCRIPTS_NONE) {
|
||||
try {
|
||||
final String modid = WarpDrive.MODID.toLowerCase();
|
||||
final String folderPeripheral = peripheralName.replace(modid, modid + "/");
|
||||
computer.unmount("/" + modid );
|
||||
computer.unmount("/" + folderPeripheral);
|
||||
computer.unmount("/warpupdater" );
|
||||
if (WarpDriveConfig.G_LUA_SCRIPTS == WarpDriveConfig.LUA_SCRIPTS_ALL) {
|
||||
for (final String script : CC_scripts) {
|
||||
computer.unmount("/" + script);
|
||||
}
|
||||
}
|
||||
} catch (final Exception exception) {
|
||||
exception.printStackTrace();
|
||||
WarpDrive.logger.error(String.format("Failed to unmount ComputerCraft scripts for %s %s, isFirstTick %s",
|
||||
peripheralName,
|
||||
Commons.format(world, pos),
|
||||
isFirstTick()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Optional.Method(modid = "computercraft")
|
||||
public void detach(@Nonnull final IComputerAccess computer) {
|
||||
if (isInterfaceEnabled()) {
|
||||
CC_unmount(computer);
|
||||
}
|
||||
final int id = computer.getID();
|
||||
connectedComputers.remove(id);
|
||||
CC_connectedComputers.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -417,13 +516,16 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
|
||||
// Computer abstraction methods
|
||||
protected void sendEvent(final String eventName, final Object... arguments) {
|
||||
if (!isInterfaceEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (WarpDriveConfig.LOGGING_LUA) {
|
||||
WarpDrive.logger.info(this + " Sending event '" + eventName + "'");
|
||||
}
|
||||
if (WarpDriveConfig.isComputerCraftLoaded) {
|
||||
for (final Map.Entry<Integer, IComputerAccess> integerIComputerAccessEntry : connectedComputers.entrySet()) {
|
||||
final IComputerAccess comp = integerIComputerAccessEntry.getValue();
|
||||
comp.queueEvent(eventName, arguments);
|
||||
for (final Map.Entry<Integer, IComputerAccess> integerIComputerAccessEntry : CC_connectedComputers.entrySet()) {
|
||||
final IComputerAccess computerAccess = integerIComputerAccessEntry.getValue();
|
||||
computerAccess.queueEvent(eventName, arguments);
|
||||
}
|
||||
}
|
||||
if (WarpDriveConfig.isOpenComputersLoaded) {
|
||||
|
@ -485,6 +587,20 @@ public abstract class TileEntityAbstractInterfaced extends TileEntityAbstractBas
|
|||
OC_fileSystem = FileSystem.asManagedEnvironment(FileSystem.fromClass(getClass(), WarpDrive.MODID.toLowerCase(), "lua.OpenComputers/" + peripheralName), peripheralName);
|
||||
((Component) OC_fileSystem.node()).setVisibility(Visibility.Network);
|
||||
}
|
||||
// note: we can't join the network right away, it's postponed to next tick
|
||||
}
|
||||
|
||||
@Optional.Method(modid = "opencomputers")
|
||||
private void OC_destructor() {
|
||||
if (OC_node != null) {
|
||||
if (OC_fileSystem != null) {
|
||||
OC_fileSystem.node().remove();
|
||||
OC_fileSystem = null;
|
||||
}
|
||||
OC_node.remove();
|
||||
OC_node = null;
|
||||
OC_addedToNetwork = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,6 +43,7 @@ public class TileEntityChunkLoader extends TileEntityAbstractChunkLoading {
|
|||
"bounds",
|
||||
"radius",
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
setUpgradeMaxCount(EnumComponentType.SUPERCONDUCTOR, 5);
|
||||
setUpgradeMaxCount(EnumComponentType.EMERALD_CRYSTAL, WarpDriveConfig.CHUNK_LOADER_MAX_RADIUS);
|
||||
|
|
|
@ -83,6 +83,8 @@ public class TileEntityLaser extends TileEntityAbstractLaser implements IBeamFre
|
|||
"beamFrequency",
|
||||
"getScanResult"
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
laserMedium_maxCount = WarpDriveConfig.LASER_CANNON_MAX_MEDIUMS_COUNT;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public class TileEntityLaserMedium extends TileEntityAbstractEnergy {
|
|||
super();
|
||||
|
||||
peripheralName = "warpdriveLaserMedium";
|
||||
OC_enable = false;
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,6 +29,7 @@ public class TileEntityAirGeneratorTiered extends TileEntityAbstractEnergyConsum
|
|||
peripheralName = "warpdriveAirGenerator";
|
||||
// addMethods(new String[] {
|
||||
// });
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -102,8 +102,10 @@ public class TileEntityLaserTreeFarm extends TileEntityAbstractMiner {
|
|||
"silktouch",
|
||||
"tapTrees"
|
||||
});
|
||||
laserMedium_maxCount = WarpDriveConfig.TREE_FARM_MAX_MEDIUMS_COUNT;
|
||||
CC_scripts = Arrays.asList("farm", "stop");
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
laserMedium_maxCount = WarpDriveConfig.TREE_FARM_MAX_MEDIUMS_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,14 +127,14 @@ public class TileEntityLaserTreeFarm extends TileEntityAbstractMiner {
|
|||
return;
|
||||
}
|
||||
|
||||
IBlockState blockState = world.getBlockState(pos);
|
||||
final IBlockState blockState = world.getBlockState(pos);
|
||||
if (currentState == STATE_IDLE) {
|
||||
delayTicks = 0;
|
||||
delayTargetTicks = TREE_FARM_WARMUP_DELAY_TICKS;
|
||||
updateBlockState(blockState, BlockLaserTreeFarm.MODE, EnumLaserTreeFarmMode.INACTIVE);
|
||||
|
||||
// force start if no computer control is available
|
||||
if (!WarpDriveConfig.isComputerCraftLoaded && !WarpDriveConfig.isOpenComputersLoaded) {
|
||||
if (!isInterfaceEnabled()) {
|
||||
breakLeaves = true;
|
||||
enableSilktouch = false;
|
||||
tapTrees = true;
|
||||
|
|
|
@ -73,6 +73,8 @@ public class TileEntityMiningLaser extends TileEntityAbstractMiner {
|
|||
"silktouch"
|
||||
});
|
||||
CC_scripts = Arrays.asList("mine", "stop");
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
laserMedium_maxCount = WarpDriveConfig.MINING_LASER_MAX_MEDIUMS_COUNT;
|
||||
}
|
||||
|
||||
|
@ -97,7 +99,7 @@ public class TileEntityMiningLaser extends TileEntityAbstractMiner {
|
|||
updateBlockState(blockState, BlockMiningLaser.MODE, EnumMiningLaserMode.INACTIVE);
|
||||
|
||||
// force start if no computer control is available
|
||||
if (!WarpDriveConfig.isComputerCraftLoaded && !WarpDriveConfig.isOpenComputersLoaded) {
|
||||
if (!isInterfaceEnabled()) {
|
||||
enableSilktouch = false;
|
||||
layerOffset = 1;
|
||||
mineAllBlocks = true;
|
||||
|
|
|
@ -38,6 +38,7 @@ public class TileEntityCamera extends TileEntityAbstractMachine implements IVide
|
|||
addMethods(new String[] {
|
||||
"videoChannel"
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,6 +34,7 @@ public class TileEntityMonitor extends TileEntityAbstractMachine implements IVid
|
|||
addMethods(new String[] {
|
||||
"videoChannel"
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -271,8 +271,8 @@ public class TileEntityRadar extends TileEntityAbstractEnergyConsumer {
|
|||
// ComputerCraft IPeripheral methods
|
||||
@Override
|
||||
@Optional.Method(modid = "computercraft")
|
||||
public void attach(@Nonnull final IComputerAccess computer) {
|
||||
super.attach(computer);
|
||||
public void attach(@Nonnull final IComputerAccess computerAccess) {
|
||||
super.attach(computerAccess);
|
||||
if (getBlockMetadata() == 0) {
|
||||
updateBlockState(null, BlockRadar.MODE, EnumRadarMode.ACTIVE);
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ public class TileEntityRadar extends TileEntityAbstractEnergyConsumer {
|
|||
@Optional.Method(modid = "computercraft")
|
||||
public void detach(@Nonnull final IComputerAccess computer) {
|
||||
super.detach(computer);
|
||||
if (connectedComputers.isEmpty()) {
|
||||
if (CC_connectedComputers.isEmpty()) {
|
||||
updateBlockState(null, BlockRadar.MODE, EnumRadarMode.INACTIVE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public class TileEntitySiren extends TileEntityAbstractMachine {
|
|||
|
||||
public TileEntitySiren() {
|
||||
super();
|
||||
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,7 @@ public class TileEntityCapacitor extends TileEntityAbstractEnergy {
|
|||
super();
|
||||
|
||||
peripheralName = "warpdriveCapacitor";
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
setUpgradeMaxCount(EnumComponentType.SUPERCONDUCTOR, WarpDriveConfig.CAPACITOR_EFFICIENCY_PER_UPGRADE.length - 1);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class TileEntityIC2reactorLaserMonitor extends TileEntityAbstractLaser {
|
|||
|
||||
laserMedium_maxCount = 1;
|
||||
peripheralName = "warpdriveIC2reactorLaserCooler";
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
// returns IReactor tile entities
|
||||
|
|
|
@ -133,6 +133,7 @@ public class TileEntityForceFieldProjector extends TileEntityAbstractForceField
|
|||
"translation"
|
||||
});
|
||||
CC_scripts = Arrays.asList("enable", "disable");
|
||||
doRequireUpgradeToInterface();
|
||||
|
||||
for (final EnumForceFieldUpgrade enumForceFieldUpgrade : EnumForceFieldUpgrade.values()) {
|
||||
if (enumForceFieldUpgrade.maxCountOnProjector > 0) {
|
||||
|
|
|
@ -24,6 +24,7 @@ public class TileEntityForceFieldRelay extends TileEntityAbstractForceField impl
|
|||
super();
|
||||
|
||||
peripheralName = "warpdriveForceFieldRelay";
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
// onFirstUpdateTick
|
||||
|
|
|
@ -53,6 +53,7 @@ public class TileEntityLift extends TileEntityAbstractEnergyConsumer implements
|
|||
"mode",
|
||||
"state"
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -49,6 +49,7 @@ public class TileEntityTransporterBeacon extends TileEntityAbstractEnergyConsume
|
|||
addMethods(new String[] {
|
||||
"isActive"
|
||||
});
|
||||
doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,6 +38,7 @@ public class TileEntityLaserCamera extends TileEntityLaser implements IVideoChan
|
|||
addMethods(new String[] {
|
||||
"videoChannel"
|
||||
});
|
||||
// done by ancestor: doRequireUpgradeToInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Add table
Reference in a new issue