diff --git a/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java
index 3aae3842b..3e087a7f6 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/BungeeSystem.java
@@ -8,6 +8,7 @@ import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.api.BungeeAPI;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.data.plugin.HookHandler;
+import com.djrapitops.plan.system.cache.BungeeCacheSystem;
import com.djrapitops.plan.system.database.BungeeDBSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.file.FileSystem;
@@ -37,6 +38,7 @@ public class BungeeSystem extends PlanSystem {
fileSystem = new FileSystem(plugin);
configSystem = new BungeeConfigSystem();
databaseSystem = new BungeeDBSystem();
+ cacheSystem = new BungeeCacheSystem(this);
listenerSystem = new BungeeListenerSystem(plugin);
taskSystem = new BungeeTaskSystem(plugin);
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java
index 950b83ba9..536f084e5 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java
@@ -36,7 +36,7 @@ public abstract class PlanSystem implements SubSystem {
// Initialized in this class
private Processing processing;
protected final WebServerSystem webServerSystem;
- protected final CacheSystem cacheSystem;
+ protected CacheSystem cacheSystem;
// These need to be initialized in the sub class.
protected VersionCheckSystem versionCheckSystem;
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/SpongeSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/SpongeSystem.java
index b54d8029e..74d7dd87d 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/SpongeSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/SpongeSystem.java
@@ -29,8 +29,6 @@ import com.djrapitops.plugin.api.utility.log.Log;
*/
public class SpongeSystem extends PlanSystem implements ServerSystem {
- private boolean firstInstall = false;
-
public SpongeSystem(PlanSponge plugin) {
setTestSystem(this);
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java
new file mode 100644
index 000000000..3babf2a10
--- /dev/null
+++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeCacheSystem.java
@@ -0,0 +1,18 @@
+package com.djrapitops.plan.system.cache;
+
+import com.djrapitops.plan.system.PlanSystem;
+
+/**
+ * CacheSystem for Bungee.
+ *
+ * Used for overriding {@link DataCache} with {@link BungeeDataCache}
+ *
+ * @author Rsl1122
+ */
+public class BungeeCacheSystem extends CacheSystem {
+
+ public BungeeCacheSystem(PlanSystem system) {
+ super(new BungeeDataCache(system));
+ }
+
+}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java
new file mode 100644
index 000000000..56eaea59e
--- /dev/null
+++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/BungeeDataCache.java
@@ -0,0 +1,25 @@
+package com.djrapitops.plan.system.cache;
+
+import com.djrapitops.plan.system.PlanSystem;
+
+import java.util.UUID;
+
+/**
+ * Bungee specific DataCache.
+ *
+ * Used for overriding {@link SessionCache#endSession(UUID, long)}.
+ *
+ * @author Rsl1122
+ */
+public class BungeeDataCache extends DataCache {
+
+ public BungeeDataCache(PlanSystem system) {
+ super(system);
+ }
+
+ @Override
+ public void endSession(UUID uuid, long time) {
+ removeSessionFromCache(uuid);
+ /* Bungee should not save sessions so session is not removed.. */
+ }
+}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java
index 522698c37..41b57d069 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java
@@ -20,7 +20,11 @@ public class CacheSystem implements SubSystem {
private final GeolocationCache geolocationCache;
public CacheSystem(PlanSystem system) {
- dataCache = new DataCache(system);
+ this(new DataCache(system));
+ }
+
+ protected CacheSystem(DataCache dataCache) {
+ this.dataCache = dataCache;
geolocationCache = new GeolocationCache();
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/DataCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/DataCache.java
index 8039ad9cc..7218465c1 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/cache/DataCache.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/DataCache.java
@@ -72,6 +72,11 @@ public class DataCache extends SessionCache implements SubSystem {
/**
* Used to get the player name in the cache.
*
+ * It is recommended to use
+ * {@link com.djrapitops.plan.data.store.keys.AnalysisKeys#PLAYER_NAMES} and
+ * {@link com.djrapitops.plan.data.store.keys.PlayerKeys#NAME} when possible
+ * because this method will call database if a name is not found.
+ *
* @param uuid UUID of the player.
* @return name or null if not cached.
*/
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java b/Plan/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java
index 2def82cea..dbd86adee 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java
@@ -2,6 +2,7 @@ package com.djrapitops.plan.system.cache;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.Session;
+import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plugin.api.utility.log.Log;
@@ -33,13 +34,6 @@ public class SessionCache {
return dataCache;
}
- /**
- * Used to get the Map of active sessions.
- *
- * Used for testing.
- *
- * @return key:value UUID:Session
- */
public static Map getActiveSessions() {
return activeSessions;
}
@@ -48,28 +42,9 @@ public class SessionCache {
activeSessions.clear();
}
- public void cacheSession(UUID uuid, Session session) {
- activeSessions.put(uuid, session);
- }
-
- public void endSession(UUID uuid, long time) {
- try {
- Session session = activeSessions.get(uuid);
- if (session == null) {
- return;
- }
- session.endSession(time);
- Database.getActive().save().session(uuid, session);
- } catch (DBOpException e) {
- Log.toLog(this.getClass(), e);
- } finally {
- activeSessions.remove(uuid);
- }
- }
-
public static void refreshActiveSessionsState() {
for (Session session : activeSessions.values()) {
- session.getWorldTimes().updateState(System.currentTimeMillis());
+ session.getUnsafe(SessionKeys.WORLD_TIMES).updateState(System.currentTimeMillis());
}
}
@@ -77,14 +52,42 @@ public class SessionCache {
* Used to get the Session of the player in the sessionCache.
*
* @param uuid UUID of the player.
- * @return Session or null if not cached.
+ * @return Optional with the session inside it if found.
*/
public static Optional getCachedSession(UUID uuid) {
- Session session = activeSessions.get(uuid);
- if (session != null) {
- return Optional.of(session);
- }
- return Optional.empty();
+ return Optional.ofNullable(activeSessions.get(uuid));
}
+ public static boolean isOnline(UUID uuid) {
+ return getCachedSession(uuid).isPresent();
+ }
+
+ public void cacheSession(UUID uuid, Session session) {
+ if (getCachedSession(uuid).isPresent()) {
+ endSession(uuid, System.currentTimeMillis());
+ }
+ activeSessions.put(uuid, session);
+ }
+
+ public void endSession(UUID uuid, long time) {
+ Session session = activeSessions.get(uuid);
+ if (session == null) {
+ return;
+ }
+ if (session.getUnsafe(SessionKeys.START) > time) {
+ return;
+ }
+ try {
+ session.endSession(time);
+ Database.getActive().save().session(uuid, session);
+ } catch (DBOpException e) {
+ Log.toLog(this.getClass(), e);
+ } finally {
+ removeSessionFromCache(uuid);
+ }
+ }
+
+ protected void removeSessionFromCache(UUID uuid) {
+ activeSessions.remove(uuid);
+ }
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java
index 91c7e0492..acf13549f 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/BungeeInfoSystem.java
@@ -9,6 +9,8 @@ import com.djrapitops.plan.api.exceptions.connection.NoServersException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.api.exceptions.connection.WebFailException;
import com.djrapitops.plan.system.info.connection.BungeeConnectionSystem;
+import com.djrapitops.plan.system.info.request.CacheRequest;
+import com.djrapitops.plan.system.info.request.GenerateInspectPageRequest;
import com.djrapitops.plan.system.info.request.InfoRequest;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.pages.parsing.NetworkPage;
@@ -29,8 +31,13 @@ public class BungeeInfoSystem extends InfoSystem {
@Override
public void runLocally(InfoRequest infoRequest) throws WebException {
- // runLocally is called when ConnectionSystem has no servers.
- throw new NoServersException("No servers were available to process this request (Local attempt): " + infoRequest.getClass().getSimpleName());
+ if (infoRequest instanceof CacheRequest ||
+ infoRequest instanceof GenerateInspectPageRequest) {
+ infoRequest.runLocally();
+ } else {
+ // runLocally is called when ConnectionSystem has no servers.
+ throw new NoServersException("No servers were available to process this request (Local attempt): " + infoRequest.getClass().getSimpleName());
+ }
}
@Override
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
index c925e55b4..e99b0ad43 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
@@ -61,7 +61,7 @@ public abstract class InfoSystem implements SubSystem {
try {
sendRequest(infoRequest);
} catch (ConnectionFailException e) {
- connectionSystem.sendWideInfoRequest(infoRequest);
+ runLocally(infoRequest);
}
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java
index 52be57380..e87255bad 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/BungeeConnectionSystem.java
@@ -9,6 +9,7 @@ import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.request.*;
import com.djrapitops.plan.system.info.server.Server;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.WebServerSystem;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.api.utility.log.Log;
@@ -51,12 +52,8 @@ public class BungeeConnectionSystem extends ConnectionSystem {
UUID serverUUID = ((GenerateAnalysisPageRequest) infoRequest).getServerUUID();
server = bukkitServers.get(serverUUID);
} else if (infoRequest instanceof GenerateInspectPageRequest) {
- Optional serverUUID = getServerWherePlayerIsOnline((GenerateInspectPageRequest) infoRequest);
- if (serverUUID.isPresent()) {
- server = bukkitServers.getOrDefault(serverUUID.get(), getOneBukkitServer());
- } else {
- server = getOneBukkitServer();
- }
+ // Run locally
+ server = ServerInfo.getServer();
}
if (server == null) {
throw new NoServersException("Proper server is not available to process request: " + infoRequest.getClass().getSimpleName());
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java
index 4ac15d21c..70a4054b2 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java
@@ -4,19 +4,13 @@
*/
package com.djrapitops.plan.system.info.connection;
-import com.djrapitops.plan.Plan;
import com.djrapitops.plan.api.exceptions.connection.NoServersException;
-import com.djrapitops.plan.api.exceptions.connection.UnsupportedTransferDatabaseException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
-import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.SubSystem;
-import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.info.request.*;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
-import com.djrapitops.plugin.api.Check;
-import com.djrapitops.plugin.api.utility.log.Log;
import com.djrapitops.plugin.utilities.Verify;
import java.util.*;
@@ -93,23 +87,6 @@ public abstract class ConnectionSystem implements SubSystem {
public abstract void sendWideInfoRequest(WideRequest infoRequest) throws NoServersException;
- protected Optional getServerWherePlayerIsOnline(GenerateInspectPageRequest infoRequest) {
- UUID playerUUID = infoRequest.getPlayerUUID();
-
- if (Check.isBukkitAvailable() && Plan.getInstance().getServer().getPlayer(playerUUID) != null) {
- return Optional.of(ServerInfo.getServerUUID());
- }
-
- try {
- return Database.getActive().transfer().getServerPlayerIsOnlineOn(playerUUID);
- } catch (UnsupportedTransferDatabaseException e) {
- /* Do nothing */
- } catch (DBOpException e) {
- Log.toLog(this.getClass(), e);
- }
- return Optional.empty();
- }
-
private Map loadDataRequests() {
Map requests = new HashMap<>();
putRequest(requests, CacheInspectPageRequest.createHandler());
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java
index 13360bac1..04064e4b3 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java
@@ -57,16 +57,12 @@ public class ServerConnectionSystem extends ConnectionSystem {
}
Server server = null;
- if (infoRequest instanceof CacheRequest) {
+ if (infoRequest instanceof CacheRequest ||
+ infoRequest instanceof GenerateInspectPageRequest) {
server = mainServer;
} else if (infoRequest instanceof GenerateAnalysisPageRequest) {
UUID serverUUID = ((GenerateAnalysisPageRequest) infoRequest).getServerUUID();
server = bukkitServers.get(serverUUID);
- } else if (infoRequest instanceof GenerateInspectPageRequest) {
- Optional serverUUID = getServerWherePlayerIsOnline((GenerateInspectPageRequest) infoRequest);
- if (serverUUID.isPresent()) {
- server = bukkitServers.getOrDefault(serverUUID.get(), ServerInfo.getServer());
- }
}
if (server == null) {
throw new NoServersException("Proper server is not available to process request: " + infoRequest.getClass().getSimpleName());
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java b/Plan/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java
index 16c89fcb3..8c524d2d7 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java
@@ -22,7 +22,7 @@ import java.util.UUID;
*
* @author Rsl1122
*/
-public class GenerateInspectPageRequest extends InfoRequestWithVariables implements GenerateRequest, WideRequest {
+public class GenerateInspectPageRequest extends InfoRequestWithVariables implements GenerateRequest {
private final UUID playerUUID;
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java
index a70344050..e5d88990c 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java
@@ -4,12 +4,16 @@
*/
package com.djrapitops.plan.system.listeners.bungee;
+import com.djrapitops.plan.data.container.Session;
+import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.player.BungeePlayerRegisterProcessor;
import com.djrapitops.plan.system.processing.processors.player.IPUpdateProcessor;
import com.djrapitops.plugin.api.utility.log.Log;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
+import net.md_5.bungee.api.event.ServerDisconnectEvent;
+import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
@@ -32,6 +36,8 @@ public class PlayerOnlineListener implements Listener {
InetAddress address = player.getAddress().getAddress();
long now = System.currentTimeMillis();
+ SessionCache.getInstance().cacheSession(uuid, new Session(uuid, now, "", ""));
+
Processing.submit(new BungeePlayerRegisterProcessor(uuid, name, now,
new IPUpdateProcessor(uuid, address, now))
);
@@ -39,4 +45,30 @@ public class PlayerOnlineListener implements Listener {
Log.toLog(this.getClass(), e);
}
}
+
+ @EventHandler
+ public void onLogout(ServerDisconnectEvent event) {
+ try {
+ ProxiedPlayer player = event.getPlayer();
+ UUID uuid = player.getUniqueId();
+
+ SessionCache.getInstance().endSession(uuid, System.currentTimeMillis());
+ } catch (Exception e) {
+ Log.toLog(this.getClass(), e);
+ }
+ }
+
+ @EventHandler
+ public void onServerSwitch(ServerSwitchEvent event) {
+ try {
+ ProxiedPlayer player = event.getPlayer();
+ UUID uuid = player.getUniqueId();
+
+ long now = System.currentTimeMillis();
+ // Replaces the current session in the cache.
+ SessionCache.getInstance().cacheSession(uuid, new Session(uuid, now, "", ""));
+ } catch (Exception e) {
+ Log.toLog(this.getClass(), e);
+ }
+ }
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java
index 7d6b534ea..2d5660995 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/parsing/InspectPage.java
@@ -19,7 +19,6 @@ import com.djrapitops.plan.data.store.mutators.formatting.PlaceholderReplacer;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.databases.Database;
-import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.theme.Theme;
@@ -87,12 +86,12 @@ public class InspectPage implements Page {
replacer.put("version", MiscUtils.getPlanVersion());
replacer.put("timeZone", MiscUtils.getTimeZoneOffsetHours());
- String online = "Offline";
+ boolean online = false;
Optional activeSession = SessionCache.getCachedSession(uuid);
if (activeSession.isPresent()) {
Session session = activeSession.get();
session.setSessionID(Integer.MAX_VALUE);
- online = serverNames.get(serverUUID);
+ online = true;
container.putRawData(PlayerKeys.ACTIVE_SESSION, session);
}
@@ -182,9 +181,12 @@ public class InspectPage implements Page {
container.getValue(PlayerKeys.BANNED).orElse(false),
container.getValue(PlayerKeys.OPERATOR).orElse(false)));
- if (!InfoSystem.getInstance().getConnectionSystem().isServerAvailable()) {
- replacer.put("networkName", Settings.SERVER_NAME.toString().replaceAll("[^a-zA-Z0-9_\\s]", "_"));
- }
+ String serverName = serverNames.get(serverUUID);
+ replacer.put("networkName",
+ serverName.equalsIgnoreCase("bungee")
+ ? Settings.BUNGEE_NETWORK_NAME.toString()
+ : serverName
+ );
return replacer.apply(FileUtil.getStringFromResource("web/player.html"));
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java b/Plan/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java
index 7b23dbe9c..810de7303 100644
--- a/Plan/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java
+++ b/Plan/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java
@@ -165,18 +165,14 @@ public class HtmlStructure {
"})";
}
- public static String playerStatus(String online, boolean banned, boolean op) {
- boolean offline = "offline".equalsIgnoreCase(online);
-
+ public static String playerStatus(boolean online, boolean banned, boolean op) {
StringBuilder html = new StringBuilder("");
- if (offline) {
+ if (online) {
+ html.append(Icon.called("circle").of(Color.GREEN))
+ .append(" Online");
+ } else {
html.append(Icon.called("circle").of(Color.RED))
.append(" Offline");
- } else {
- html.append(Icon.called("circle").of(Color.GREEN))
- .append(" Online (")
- .append(online)
- .append(")");
}
html.append("
");
if (op) {