forked from I2P_Developers/i2p.i2p
Transports: Convert internal state to enums (ticket #1458)
This commit is contained in:
@@ -64,9 +64,17 @@ public abstract class CommSystemFacade implements Service {
|
||||
|
||||
/**
|
||||
* Determine under what conditions we are remotely reachable.
|
||||
*
|
||||
* For internal use only.
|
||||
* Not recommended for plugins or embedded applications, as
|
||||
* the integer codes may change. Use getStatus() instead.
|
||||
*/
|
||||
public short getReachabilityStatus() { return STATUS_OK; }
|
||||
public short getReachabilityStatus() { return (short) getStatus().getCode(); }
|
||||
|
||||
/**
|
||||
* Determine under what conditions we are remotely reachable.
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public Status getStatus() { return Status.OK; }
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.message.GarlicMessageHandler;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.startup.CreateRouterInfoJob;
|
||||
@@ -919,17 +920,20 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
ri.addCapability(CAPABILITY_UNREACHABLE);
|
||||
return;
|
||||
}
|
||||
switch (_context.commSystem().getReachabilityStatus()) {
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
switch (_context.commSystem().getStatus()) {
|
||||
case OK:
|
||||
ri.addCapability(CAPABILITY_REACHABLE);
|
||||
break;
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
|
||||
case DIFFERENT:
|
||||
case REJECT_UNSOLICITED:
|
||||
ri.addCapability(CAPABILITY_UNREACHABLE);
|
||||
break;
|
||||
case CommSystemFacade.STATUS_DISCONNECTED:
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
case CommSystemFacade.STATUS_UNKNOWN:
|
||||
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
// no explicit capability
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -176,13 +176,16 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
return _manager.getMostRecentErrorMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public short getReachabilityStatus() {
|
||||
public Status getStatus() {
|
||||
if (!_netMonitorStatus)
|
||||
return STATUS_DISCONNECTED;
|
||||
short rv = _manager.getReachabilityStatus();
|
||||
if (rv != STATUS_HOSED && _context.router().isHidden())
|
||||
return STATUS_OK;
|
||||
return Status.DISCONNECTED;
|
||||
Status rv = _manager.getReachabilityStatus();
|
||||
if (rv != Status.HOSED && _context.router().isHidden())
|
||||
return Status.OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Vector;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
|
||||
/**
|
||||
@@ -131,7 +132,11 @@ public interface Transport {
|
||||
public List<String> getMostRecentErrorMessages();
|
||||
|
||||
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException;
|
||||
public short getReachabilityStatus();
|
||||
|
||||
/**
|
||||
* Previously returned short, now enum as of 0.9.20
|
||||
*/
|
||||
public Status getReachabilityStatus();
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
|
||||
@@ -34,7 +34,7 @@ import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.MessageSelector;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
@@ -726,7 +726,10 @@ public abstract class TransportImpl implements Transport {
|
||||
public void renderStatusHTML(Writer out) throws IOException {}
|
||||
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { renderStatusHTML(out); }
|
||||
|
||||
public short getReachabilityStatus() { return CommSystemFacade.STATUS_UNKNOWN; }
|
||||
/**
|
||||
* Previously returned short, now enum as of 0.9.20
|
||||
*/
|
||||
public abstract Status getReachabilityStatus();
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
@@ -752,9 +755,9 @@ public abstract class TransportImpl implements Transport {
|
||||
|
||||
/** called when we can't reach a peer */
|
||||
public void markUnreachable(Hash peer) {
|
||||
short status = _context.commSystem().getReachabilityStatus();
|
||||
if (status == CommSystemFacade.STATUS_DISCONNECTED ||
|
||||
status == CommSystemFacade.STATUS_HOSED)
|
||||
Status status = _context.commSystem().getStatus();
|
||||
if (status == Status.DISCONNECTED ||
|
||||
status == Status.HOSED)
|
||||
return;
|
||||
Long now = Long.valueOf(_context.clock().now());
|
||||
synchronized (_unreachableEntries) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
import static net.i2p.router.transport.Transport.AddressSource.*;
|
||||
@@ -371,12 +371,15 @@ public class TransportManager implements TransportEventListener {
|
||||
return skews;
|
||||
}
|
||||
|
||||
/** @return the best status of any transport */
|
||||
public short getReachabilityStatus() {
|
||||
short rv = CommSystemFacade.STATUS_UNKNOWN;
|
||||
/**
|
||||
* Previously returned short, now enum as of 0.9.20
|
||||
* @return the best status of any transport
|
||||
*/
|
||||
public Status getReachabilityStatus() {
|
||||
Status rv = Status.UNKNOWN;
|
||||
for (Transport t : _transports.values()) {
|
||||
short s = t.getReachabilityStatus();
|
||||
if (s < rv)
|
||||
Status s = t.getReachabilityStatus();
|
||||
if (s.getCode() < rv.getCode())
|
||||
rv = s;
|
||||
}
|
||||
return rv;
|
||||
|
||||
@@ -31,7 +31,7 @@ import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.Transport;
|
||||
@@ -1162,17 +1162,18 @@ public class NTCPTransport extends TransportImpl {
|
||||
*
|
||||
* We have to be careful here because much of the router console code assumes
|
||||
* that the reachability status is really just the UDP status.
|
||||
*
|
||||
* Previously returned short, now enum as of 0.9.20
|
||||
*/
|
||||
@Override
|
||||
public short getReachabilityStatus() {
|
||||
public Status getReachabilityStatus() {
|
||||
// If we have an IPv4 address
|
||||
if (isAlive() && getCurrentAddress(false) != null) {
|
||||
for (NTCPConnection con : _conByIdent.values()) {
|
||||
if (con.isInbound())
|
||||
return CommSystemFacade.STATUS_OK;
|
||||
return Status.OK;
|
||||
}
|
||||
}
|
||||
return CommSystemFacade.STATUS_UNKNOWN;
|
||||
return Status.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.RouterContext;
|
||||
import static net.i2p.router.transport.udp.PeerTestState.Role.*;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
@@ -364,7 +364,7 @@ class PeerTestManager {
|
||||
// why are we doing this instead of calling testComplete() ?
|
||||
_currentTestComplete = true;
|
||||
_context.statManager().addRateData("udp.statusKnownCharlie", 1);
|
||||
honorStatus(CommSystemFacade.STATUS_UNKNOWN);
|
||||
honorStatus(Status.UNKNOWN);
|
||||
_currentTest = null;
|
||||
return;
|
||||
}
|
||||
@@ -433,7 +433,6 @@ class PeerTestManager {
|
||||
*/
|
||||
private void testComplete(boolean forgetTest) {
|
||||
_currentTestComplete = true;
|
||||
short status = -1;
|
||||
PeerTestState test = _currentTest;
|
||||
|
||||
// Don't do this or we won't call honorStatus()
|
||||
@@ -443,25 +442,26 @@ class PeerTestManager {
|
||||
// return;
|
||||
// }
|
||||
|
||||
Status status;
|
||||
if (test.getAlicePortFromCharlie() > 0) {
|
||||
// we received a second message from charlie
|
||||
if ( (test.getAlicePort() == test.getAlicePortFromCharlie()) &&
|
||||
(test.getAliceIP() != null) && (test.getAliceIPFromCharlie() != null) &&
|
||||
(test.getAliceIP().equals(test.getAliceIPFromCharlie())) ) {
|
||||
status = CommSystemFacade.STATUS_OK;
|
||||
status = Status.OK;
|
||||
} else {
|
||||
status = CommSystemFacade.STATUS_DIFFERENT;
|
||||
status = Status.DIFFERENT;
|
||||
}
|
||||
} else if (test.getReceiveCharlieTime() > 0) {
|
||||
// we received only one message from charlie
|
||||
status = CommSystemFacade.STATUS_UNKNOWN;
|
||||
status = Status.UNKNOWN;
|
||||
} else if (test.getReceiveBobTime() > 0) {
|
||||
// we received a message from bob but no messages from charlie
|
||||
status = CommSystemFacade.STATUS_REJECT_UNSOLICITED;
|
||||
status = Status.REJECT_UNSOLICITED;
|
||||
} else {
|
||||
// we never received anything from bob - he is either down,
|
||||
// ignoring us, or unable to get a Charlie to respond
|
||||
status = CommSystemFacade.STATUS_UNKNOWN;
|
||||
status = Status.UNKNOWN;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -477,7 +477,7 @@ class PeerTestManager {
|
||||
* necessary).
|
||||
*
|
||||
*/
|
||||
private void honorStatus(short status) {
|
||||
private void honorStatus(Status status) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Test results: status = " + status);
|
||||
_transport.setReachabilityStatus(status);
|
||||
|
||||
@@ -30,6 +30,7 @@ import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
@@ -77,7 +78,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private final ExpirePeerEvent _expireEvent;
|
||||
private final PeerTestEvent _testEvent;
|
||||
private final PacketBuilder _destroyBuilder;
|
||||
private short _reachabilityStatus;
|
||||
private Status _reachabilityStatus;
|
||||
private long _reachabilityStatusLastUpdated;
|
||||
private int _reachabilityStatusUnchanged;
|
||||
private long _introducersSelectedOn;
|
||||
@@ -245,7 +246,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_expireTimeout = EXPIRE_TIMEOUT;
|
||||
_expireEvent = new ExpirePeerEvent();
|
||||
_testEvent = new PeerTestEvent();
|
||||
_reachabilityStatus = CommSystemFacade.STATUS_UNKNOWN;
|
||||
_reachabilityStatus = Status.UNKNOWN;
|
||||
_introManager = new IntroductionManager(_context, this);
|
||||
_introducersSelectedOn = -1;
|
||||
_lastInboundReceivedOn = -1;
|
||||
@@ -430,7 +431,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
if (_endpoints.isEmpty()) {
|
||||
_log.log(Log.CRIT, "Unable to open UDP port");
|
||||
setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
|
||||
setReachabilityStatus(Status.HOSED);
|
||||
return;
|
||||
}
|
||||
if (newPort > 0 &&
|
||||
@@ -508,7 +509,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_log.log(Log.CRIT, "UDP port failure: " + endpoint);
|
||||
if (_endpoints.isEmpty()) {
|
||||
_log.log(Log.CRIT, "No more UDP sockets open");
|
||||
setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
|
||||
setReachabilityStatus(Status.HOSED);
|
||||
// TODO restart?
|
||||
}
|
||||
rebuildExternalAddress();
|
||||
@@ -725,7 +726,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
// Assume if we have an interface with a public IP that we aren't firewalled.
|
||||
// If this is wrong, the peer test will figure it out and change the status.
|
||||
if (changed && ip.length == 4 && source == SOURCE_INTERFACE)
|
||||
setReachabilityStatus(CommSystemFacade.STATUS_OK);
|
||||
setReachabilityStatus(Status.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,7 +744,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
|
||||
}
|
||||
if (success && ip != null && getExternalIP() != null)
|
||||
setReachabilityStatus(CommSystemFacade.STATUS_OK);
|
||||
setReachabilityStatus(Status.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -980,8 +981,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
String prop = _context.getProperty(PROP_FIXED_PORT);
|
||||
if (prop != null)
|
||||
return Boolean.parseBoolean(prop);
|
||||
int status = getReachabilityStatus();
|
||||
return status != CommSystemFacade.STATUS_REJECT_UNSOLICITED;
|
||||
Status status = getReachabilityStatus();
|
||||
return status != Status.REJECT_UNSOLICITED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1187,7 +1188,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
synchronized(_rebuildLock) {
|
||||
rebuildIfNecessary();
|
||||
if (getReachabilityStatus() != CommSystemFacade.STATUS_OK &&
|
||||
if (getReachabilityStatus() != Status.OK &&
|
||||
_reachabilityStatusUnchanged < 7) {
|
||||
_testEvent.forceRunSoon();
|
||||
}
|
||||
@@ -1560,7 +1561,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return _cachedBid[FAST_BID];
|
||||
} else {
|
||||
// If we don't have a port, all is lost
|
||||
if ( _reachabilityStatus == CommSystemFacade.STATUS_HOSED) {
|
||||
if ( _reachabilityStatus == Status.HOSED) {
|
||||
markUnreachable(to);
|
||||
return null;
|
||||
}
|
||||
@@ -2087,10 +2088,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return true;
|
||||
}
|
||||
*******************/
|
||||
short status = getReachabilityStatus();
|
||||
Status status = getReachabilityStatus();
|
||||
switch (status) {
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
case REJECT_UNSOLICITED:
|
||||
case DIFFERENT:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Require introducers, because our status is " + status);
|
||||
return true;
|
||||
@@ -2695,7 +2696,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
long longInactivityCutoff = now - EXPIRE_TIMEOUT;
|
||||
long pingCutoff = now - (2 * 60*60*1000);
|
||||
long pingFirewallCutoff = now - PING_FIREWALL_CUTOFF;
|
||||
boolean shouldPingFirewall = _reachabilityStatus != CommSystemFacade.STATUS_OK;
|
||||
boolean shouldPingFirewall = _reachabilityStatus != Status.OK;
|
||||
int currentListenPort = getListenPort(false);
|
||||
boolean pingOneOnly = shouldPingFirewall && getExternalPort(false) == currentListenPort;
|
||||
boolean shortLoop = shouldPingFirewall;
|
||||
@@ -2772,17 +2773,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
}
|
||||
|
||||
void setReachabilityStatus(short status) {
|
||||
void setReachabilityStatus(Status status) {
|
||||
synchronized (_rebuildLock) {
|
||||
locked_setReachabilityStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_setReachabilityStatus(short status) {
|
||||
short old = _reachabilityStatus;
|
||||
private void locked_setReachabilityStatus(Status status) {
|
||||
Status old = _reachabilityStatus;
|
||||
long now = _context.clock().now();
|
||||
switch (status) {
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
case OK:
|
||||
// TODO if OK but internal port != external port, should we have
|
||||
// a different status state? ...as we don't know if the TCP
|
||||
// port will be mapped the same way or not...
|
||||
@@ -2791,22 +2792,22 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_reachabilityStatus = status;
|
||||
_reachabilityStatusLastUpdated = now;
|
||||
break;
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
case DIFFERENT:
|
||||
_context.statManager().addRateData("udp.statusDifferent", 1);
|
||||
_reachabilityStatus = status;
|
||||
_reachabilityStatusLastUpdated = now;
|
||||
break;
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
case REJECT_UNSOLICITED:
|
||||
_context.statManager().addRateData("udp.statusReject", 1);
|
||||
// if old != unsolicited && now - lastUpdated > STATUS_GRACE_PERIOD)
|
||||
//
|
||||
// fall through...
|
||||
case CommSystemFacade.STATUS_DISCONNECTED:
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
_reachabilityStatus = status;
|
||||
_reachabilityStatusLastUpdated = now;
|
||||
break;
|
||||
case CommSystemFacade.STATUS_UNKNOWN:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
_context.statManager().addRateData("udp.statusUnknown", 1);
|
||||
//if (now - _reachabilityStatusLastUpdated < STATUS_GRACE_PERIOD) {
|
||||
@@ -2819,17 +2820,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
break;
|
||||
}
|
||||
_testEvent.setLastTested();
|
||||
if (status != CommSystemFacade.STATUS_UNKNOWN) {
|
||||
if (status != Status.UNKNOWN) {
|
||||
if (status != old)
|
||||
_reachabilityStatusUnchanged = 0;
|
||||
else
|
||||
_reachabilityStatusUnchanged++;
|
||||
}
|
||||
if ( (status != old) && (status != CommSystemFacade.STATUS_UNKNOWN) ) {
|
||||
if ( (status != old) && (status != Status.UNKNOWN) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Old status: " + old + " New status: " + status + " from: ", new Exception("traceback"));
|
||||
if (old != CommSystemFacade.STATUS_UNKNOWN)
|
||||
_context.router().eventLog().addEvent(EventLog.REACHABILITY, Integer.toString(status));
|
||||
if (old != Status.UNKNOWN)
|
||||
_context.router().eventLog().addEvent(EventLog.REACHABILITY, status.toStatusString());
|
||||
// Always rebuild when the status changes, even if our address hasn't changed,
|
||||
// as rebuildExternalAddress() calls replaceAddress() which calls CSFI.notifyReplaceAddress()
|
||||
// which will start up NTCP inbound when we transition to OK.
|
||||
@@ -2844,18 +2845,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
private static final String PROP_REACHABILITY_STATUS_OVERRIDE = "i2np.udp.status";
|
||||
|
||||
@Override
|
||||
public short getReachabilityStatus() {
|
||||
/**
|
||||
* Previously returned short, now enum as of 0.9.20
|
||||
*/
|
||||
public Status getReachabilityStatus() {
|
||||
String override = _context.getProperty(PROP_REACHABILITY_STATUS_OVERRIDE);
|
||||
if (override == null)
|
||||
return _reachabilityStatus;
|
||||
|
||||
if ("ok".equals(override))
|
||||
return CommSystemFacade.STATUS_OK;
|
||||
return Status.OK;
|
||||
else if ("err-reject".equals(override))
|
||||
return CommSystemFacade.STATUS_REJECT_UNSOLICITED;
|
||||
return Status.REJECT_UNSOLICITED;
|
||||
else if ("err-different".equals(override))
|
||||
return CommSystemFacade.STATUS_DIFFERENT;
|
||||
return Status.DIFFERENT;
|
||||
|
||||
return _reachabilityStatus;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user