diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 7ae5ed1b..408bfc92 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -26,25 +26,14 @@ jobs: # Pull changes - uses: actions/checkout@v2.3.4 - # Cache artifacts - however this has the downside that we don't get notified of - # artifact resolution failures like invalid repository - # Nevertheless the repositories should be more stable and it makes no sense to pull - # a same version every time - # A dry run would make more sense - - uses: actions/cache@v2.1.4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - # Setup Java - name: Set up JDK - uses: actions/setup-java@v2.1.0 + uses: actions/setup-java@v2.3.0 with: distribution: 'adopt' # Use Java 11, because it's minimum required version java-version: 11 + cache: 'maven' # Build and test (included in package) - name: Build with Maven and test diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 8e048961..4c89a7c4 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -252,6 +252,7 @@ database: '{pluginDir}/FastLogin.db' # MySQL/MariaDB # If you want to enable it uncomment only the lines below this not this line. +# If on velocity use 'fastlogin.mysql.cj.jdbc.Driver' as driver #driver: 'com.mysql.jdbc.Driver' #host: '127.0.0.1' #port: 3306 diff --git a/pom.xml b/pom.xml index 6031c250..f3971014 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,7 @@ core bukkit bungee + velocity diff --git a/velocity/pom.xml b/velocity/pom.xml new file mode 100644 index 00000000..64582e85 --- /dev/null +++ b/velocity/pom.xml @@ -0,0 +1,133 @@ + + + 4.0.0 + + + com.github.games647 + fastlogin + 1.11-SNAPSHOT + ../pom.xml + + + + fastlogin.velocity + jar + + + FastLoginVelocity + + + + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filter-src + + filter-sources + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + false + false + + + com.zaxxer.hikari + fastlogin.hikari + + + net.md_5.bungee.config + fastlogin.config + + + com.mysql + fastlogin.mysql + + + + + + org.slf4j:* + com.google.code.gson:gson + + + + + + package + + shade + + + + + + + + + + velocity + https://nexus.velocitypowered.com/repository/maven-public/ + + + + + + + ${project.groupId} + fastlogin.core + ${project.version} + + + + + com.velocitypowered + velocity-api + 3.0.1 + provided + + + + + org.mariadb.jdbc + mariadb-java-client + 2.7.4 + + + diff --git a/velocity/src/main/java-templates/com/github/games647/fastlogin/velocity/PomData.java b/velocity/src/main/java-templates/com/github/games647/fastlogin/velocity/PomData.java new file mode 100644 index 00000000..340e0ea7 --- /dev/null +++ b/velocity/src/main/java-templates/com/github/games647/fastlogin/velocity/PomData.java @@ -0,0 +1,9 @@ +package com.github.games647.fastlogin.velocity; + +public class PomData { + public static final String DISPLAY_NAME = "${project.name}"; + public static final String NAME = "${project.parent.artifactId}"; + public static final String VERSION = "${project.version}-${git.commit.id.abbrev}"; + public static final String DESCRIPTION = "${project.parent.description}"; + public static final String URL = "${project.parent.url}"; +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java new file mode 100644 index 00000000..9de54b5a --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java @@ -0,0 +1,199 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity; + +import com.github.games647.fastlogin.core.AsyncScheduler; +import com.github.games647.fastlogin.core.message.ChangePremiumMessage; +import com.github.games647.fastlogin.core.message.ChannelMessage; +import com.github.games647.fastlogin.core.message.SuccessMessage; +import com.github.games647.fastlogin.core.shared.FastLoginCore; +import com.github.games647.fastlogin.core.shared.PlatformPlugin; +import com.github.games647.fastlogin.velocity.listener.ConnectListener; +import com.github.games647.fastlogin.velocity.listener.PluginMessageListener; +import com.google.common.collect.MapMaker; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import com.google.inject.Inject; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import com.velocitypowered.api.proxy.server.RegisteredServer; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentMap; + +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import org.slf4j.Logger; + +//TODO: Support for floodgate +@Plugin(id = PomData.NAME, name = PomData.DISPLAY_NAME, description = PomData.DESCRIPTION, url = PomData.URL, + version = PomData.VERSION, authors = {"games647", "https://github.com/games647/FastLogin/graphs/contributors"}) +public class FastLoginVelocity implements PlatformPlugin { + + private final ProxyServer server; + private final Path dataDirectory; + private final Logger logger; + private final ConcurrentMap session = new MapMaker().weakKeys().makeMap(); + private static final String PROXY_ID_fILE = "proxyId.txt"; + + private FastLoginCore core; + private AsyncScheduler scheduler; + private UUID proxyId; + + @Inject + public FastLoginVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { + this.server = server; + this.logger = logger; + this.dataDirectory = dataDirectory; + } + + @Subscribe + public void onProxyInitialization(ProxyInitializeEvent event) { + scheduler = new AsyncScheduler(logger, getThreadFactory()); + core = new FastLoginCore<>(this); + core.load(); + loadOrGenerateProxyId(); + if (!core.setupDatabase() || proxyId == null) { + return; + } + + server.getEventManager().register(this, new ConnectListener(this, core.getRateLimiter())); + server.getEventManager().register(this, new PluginMessageListener(this)); + server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), ChangePremiumMessage.CHANGE_CHANNEL)); + server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), SuccessMessage.SUCCESS_CHANNEL)); + } + + @Subscribe + public void onProxyShutdown(ProxyShutdownEvent event) { + if (core != null) { + core.close(); + } + } + + @Override + public String getName() { + return PomData.NAME; + } + + @Override + public Path getPluginFolder() { + return dataDirectory; + } + + @Override + public Logger getLog() { + return logger; + } + + @Override + public void sendMessage(CommandSource receiver, String message) { + receiver.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + } + + @Override + public AsyncScheduler getScheduler() { + return scheduler; + } + + @Override + public boolean isPluginInstalled(String name) { + return server.getPluginManager().isLoaded(name); + } + + public FastLoginCore getCore() { + return core; + } + + public ConcurrentMap getSession() { + return session; + } + + public ProxyServer getProxy() { + return server; + } + + public void sendPluginMessage(RegisteredServer server, ChannelMessage message) { + if (server != null) { + ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput(); + message.writeTo(dataOutput); + + MinecraftChannelIdentifier channel = MinecraftChannelIdentifier.create(getName(), message.getChannelName()); + server.sendPluginMessage(channel, dataOutput.toByteArray()); + } + } + + private void loadOrGenerateProxyId() { + Path idFile = dataDirectory.resolve(PROXY_ID_fILE); + boolean shouldGenerate = false; + + if (Files.exists(idFile)) { + try { + List lines = Files.readAllLines(idFile, StandardCharsets.UTF_8); + if (lines.isEmpty()) { + shouldGenerate = true; + } else { + proxyId = UUID.fromString(lines.get(0)); + } + } catch (IOException e) { + logger.error("Unable to load proxy id from '{}'", idFile.toAbsolutePath()); + logger.error("Detailed exception:", e); + } catch (IllegalArgumentException e) { + logger.error("'{}' contains an invalid uuid! FastLogin will not work without a valid id.", idFile.toAbsolutePath()); + } + } else { + shouldGenerate = true; + } + + if (shouldGenerate) { + proxyId = UUID.randomUUID(); + try { + Files.write(idFile, Collections.singletonList(proxyId.toString()), StandardOpenOption.CREATE); + } catch (IOException e) { + logger.error("Unable to save proxy id to '{}'", idFile.toAbsolutePath()); + logger.error("Detailed exception:", e); + } + } + } + + public UUID getProxyId() { + return proxyId; + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSession.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSession.java new file mode 100644 index 00000000..be5d314d --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSession.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.LoginSession; + +public class VelocityLoginSession extends LoginSession { + private boolean alreadySaved; + private boolean alreadyLogged; + + public VelocityLoginSession(String requestUsername, boolean registered, StoredProfile profile) { + super(requestUsername, registered, profile); + } + + public synchronized void setRegistered(boolean registered) { + this.registered = registered; + } + + public synchronized boolean isAlreadySaved() { + return alreadySaved; + } + + public synchronized void setAlreadySaved(boolean alreadySaved) { + this.alreadySaved = alreadySaved; + } + + public synchronized boolean isAlreadyLogged() { + return alreadyLogged; + } + + public synchronized void setAlreadyLogged(boolean alreadyLogged) { + this.alreadyLogged = alreadyLogged; + } + + @Override + public synchronized String toString() { + return this.getClass().getSimpleName() + '{' + + "alreadySaved=" + alreadySaved + + ", alreadyLogged=" + alreadyLogged + + ", registered=" + registered + + "} " + super.toString(); + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSource.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSource.java new file mode 100644 index 00000000..d2ee5102 --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/VelocityLoginSource.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity; + +import com.github.games647.fastlogin.core.shared.LoginSource; +import com.velocitypowered.api.event.connection.PreLoginEvent; +import com.velocitypowered.api.proxy.InboundConnection; + +import java.net.InetSocketAddress; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +public class VelocityLoginSource implements LoginSource { + + private final InboundConnection connection; + private final PreLoginEvent preLoginEvent; + + public VelocityLoginSource(InboundConnection connection, PreLoginEvent preLoginEvent) { + this.connection = connection; + this.preLoginEvent = preLoginEvent; + } + + @Override + public void enableOnlinemode() { + preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.forceOnlineMode()); + } + + @Override + public void kick(String message) { + if (message == null) { + preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied( + Component.text("Kicked").color(NamedTextColor.WHITE))); + } else { + preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied( + LegacyComponentSerializer.legacyAmpersand().deserialize(message))); + } + } + + @Override + public InetSocketAddress getAddress() { + return connection.getRemoteAddress(); + } + + public InboundConnection getConnection() { + return connection; + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginAutoLoginEvent.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginAutoLoginEvent.java new file mode 100644 index 00000000..61ecb1a8 --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginAutoLoginEvent.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.event; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.LoginSession; +import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; +import com.velocitypowered.api.event.ResultedEvent; + +import java.util.Objects; + +public class VelocityFastLoginAutoLoginEvent + implements FastLoginAutoLoginEvent, ResultedEvent { + + private final LoginSession session; + private final StoredProfile profile; + private boolean cancelled; + + public VelocityFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) { + this.session = session; + this.profile = profile; + } + + @Override + public LoginSession getSession() { + return session; + } + + @Override + public StoredProfile getProfile() { + return profile; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + + @Override + public GenericResult getResult() { + return cancelled ? GenericResult.denied(): GenericResult.allowed(); + } + + @Override + public void setResult(GenericResult result) { + cancelled = Objects.requireNonNull(result) != GenericResult.allowed(); + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPreLoginEvent.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPreLoginEvent.java new file mode 100644 index 00000000..ce58ee52 --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPreLoginEvent.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.event; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.LoginSource; +import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; + +public class VelocityFastLoginPreLoginEvent implements FastLoginPreLoginEvent { + + private final String username; + private final LoginSource source; + private final StoredProfile profile; + + public VelocityFastLoginPreLoginEvent(String username, LoginSource source, StoredProfile profile) { + this.username = username; + this.source = source; + this.profile = profile; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public LoginSource getSource() { + return source; + } + + @Override + public StoredProfile getProfile() { + return profile; + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPremiumToggleEvent.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPremiumToggleEvent.java new file mode 100644 index 00000000..3dc83b1a --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/event/VelocityFastLoginPremiumToggleEvent.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.event; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent; + +public class VelocityFastLoginPremiumToggleEvent implements FastLoginPremiumToggleEvent { + + private final StoredProfile profile; + private final PremiumToggleReason reason; + + public VelocityFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) { + this.profile = profile; + this.reason = reason; + } + + @Override + public StoredProfile getProfile() { + return profile; + } + + @Override + public PremiumToggleReason getReason() { + return reason; + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java new file mode 100644 index 00000000..acb24c7b --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java @@ -0,0 +1,141 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.listener; + +import com.github.games647.craftapi.UUIDAdapter; +import com.github.games647.fastlogin.core.RateLimiter; +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.LoginSession; +import com.github.games647.fastlogin.velocity.FastLoginVelocity; +import com.github.games647.fastlogin.velocity.VelocityLoginSession; +import com.github.games647.fastlogin.velocity.task.AsyncPremiumCheck; +import com.github.games647.fastlogin.velocity.task.ForceLoginTask; +import com.velocitypowered.api.event.Continuation; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PreLoginEvent; +import com.velocitypowered.api.event.player.GameProfileRequestEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.GameProfile; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class ConnectListener { + + private final FastLoginVelocity plugin; + private final RateLimiter rateLimiter; + + public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) { + this.plugin = plugin; + this.rateLimiter = rateLimiter; + } + + @Subscribe + public void onPreLogin(PreLoginEvent preLoginEvent, Continuation continuation) { + if (!preLoginEvent.getResult().isAllowed()) { + return; + } + + InboundConnection connection = preLoginEvent.getConnection(); + if (!rateLimiter.tryAcquire()) { + plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection); + return; + } + + String username = preLoginEvent.getUsername(); + plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress()); + + Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent); + plugin.getScheduler().runAsync(asyncPremiumCheck); + } + + @Subscribe + public void onGameprofileRequest(GameProfileRequestEvent event) { + if (event.isOnlineMode()) { + LoginSession session = plugin.getSession().get(event.getConnection().getRemoteAddress()); + + UUID verifiedUUID = event.getGameProfile().getId(); + String verifiedUsername = event.getUsername(); + session.setUuid(verifiedUUID); + session.setVerifiedUsername(verifiedUsername); + + StoredProfile playerProfile = session.getProfile(); + playerProfile.setId(verifiedUUID); + if (!plugin.getCore().getConfig().get("premiumUuid", true)) { + UUID offlineUUID = UUIDAdapter.generateOfflineId(playerProfile.getName()); + event.setGameProfile(event.getGameProfile().withId(offlineUUID)); + plugin.getLog().info("Overridden UUID from {} to {} (based of {}) on {}", + verifiedUUID, offlineUUID, verifiedUsername, event.getConnection()); + } + + if (!plugin.getCore().getConfig().get("forwardSkin", true)) { + event.setGameProfile(event.getGameProfile().withProperties(removeSkin(event.getGameProfile().getProperties()))); + } + } + } + + private List removeSkin(List oldProperties) { + List newProperties = new ArrayList<>(oldProperties.size() - 1); + for (GameProfile.Property property : oldProperties) { + if (!"textures".equals(property.getName())) + newProperties.add(property); + } + + return newProperties; + } + + @Subscribe + public void onServerConnected(ServerConnectedEvent serverConnectedEvent) { + Player player = serverConnectedEvent.getPlayer(); + RegisteredServer server = serverConnectedEvent.getServer(); + + VelocityLoginSession session = plugin.getSession().get(player.getRemoteAddress()); + if (session == null) { + return; + } + + // delay sending force command, because Paper will process the login event asynchronously + // In this case it means that the force command (plugin message) is already received and processed while + // player is still in the login phase and reported to be offline. + Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server, session); + plugin.getProxy().getScheduler() + .buildTask(plugin, () -> plugin.getScheduler().runAsync(loginTask)) + .delay(1L, TimeUnit.SECONDS) // Delay at least one second, otherwise the login command can be missed + .schedule(); + } + + @Subscribe + public void onDisconnect(DisconnectEvent disconnectEvent) { + Player player = disconnectEvent.getPlayer(); + plugin.getCore().getPendingConfirms().remove(player.getUniqueId()); + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/PluginMessageListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/PluginMessageListener.java new file mode 100644 index 00000000..b9df0a8a --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/PluginMessageListener.java @@ -0,0 +1,129 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.listener; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.message.ChangePremiumMessage; +import com.github.games647.fastlogin.core.message.SuccessMessage; +import com.github.games647.fastlogin.core.shared.FastLoginCore; +import com.github.games647.fastlogin.velocity.FastLoginVelocity; +import com.github.games647.fastlogin.velocity.VelocityLoginSession; +import com.github.games647.fastlogin.velocity.task.AsyncToggleMessage; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.Arrays; + +public class PluginMessageListener { + + private final FastLoginVelocity plugin; + + private final String successChannel; + private final String changeChannel; + + public PluginMessageListener(FastLoginVelocity plugin) { + this.plugin = plugin; + + this.successChannel = MinecraftChannelIdentifier.create(plugin.getName(), SuccessMessage.SUCCESS_CHANNEL).getId(); + this.changeChannel = MinecraftChannelIdentifier.create(plugin.getName(), ChangePremiumMessage.CHANGE_CHANNEL).getId(); + } + + @Subscribe + public void onPluginMessage(PluginMessageEvent pluginMessageEvent) { + String channel = pluginMessageEvent.getIdentifier().getId(); + if (!pluginMessageEvent.getResult().isAllowed() || !channel.startsWith(plugin.getName().toLowerCase())) { + return; + } + + //the client shouldn't be able to read the messages in order to know something about server internal states + //moreover the client shouldn't be able fake a running premium check by sending the result message + pluginMessageEvent.setResult(PluginMessageEvent.ForwardResult.handled()); + + if (!(pluginMessageEvent.getSource() instanceof ServerConnection)) { + //check if the message is sent from the server + return; + } + + //so that we can safely process this in the background + byte[] data = Arrays.copyOf(pluginMessageEvent.getData(), pluginMessageEvent.getData().length); + Player forPlayer = (Player) pluginMessageEvent.getTarget(); + + plugin.getScheduler().runAsync(() -> readMessage(forPlayer, channel, data)); + } + + private void readMessage(Player forPlayer, String channel, byte[] data) { + FastLoginCore core = plugin.getCore(); + + ByteArrayDataInput dataInput = ByteStreams.newDataInput(data); + if (successChannel.equals(channel)) { + onSuccessMessage(forPlayer); + } else if (changeChannel.equals(channel)) { + ChangePremiumMessage changeMessage = new ChangePremiumMessage(); + changeMessage.readFrom(dataInput); + + String playerName = changeMessage.getPlayerName(); + boolean isSourceInvoker = changeMessage.isSourceInvoker(); + if (changeMessage.shouldEnable()) { + if (playerName.equals(forPlayer.getUsername()) && plugin.getCore().getConfig().get("premium-warning", true) + && !core.getPendingConfirms().contains(forPlayer.getUniqueId())) { + String message = core.getMessage("premium-warning"); + forPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + core.getPendingConfirms().add(forPlayer.getUniqueId()); + return; + } + + core.getPendingConfirms().remove(forPlayer.getUniqueId()); + Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isSourceInvoker); + plugin.getScheduler().runAsync(task); + } else { + Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isSourceInvoker); + plugin.getScheduler().runAsync(task); + } + } + } + + private void onSuccessMessage(Player forPlayer) { + if (forPlayer.isOnlineMode()){ + //bukkit module successfully received and force logged in the user + //update only on success to prevent corrupt data + VelocityLoginSession loginSession = plugin.getSession().get(forPlayer.getRemoteAddress()); + StoredProfile playerProfile = loginSession.getProfile(); + loginSession.setRegistered(true); + if (!loginSession.isAlreadySaved()) { + playerProfile.setPremium(true); + plugin.getCore().getStorage().save(playerProfile); + loginSession.setAlreadySaved(true); + } + } + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncPremiumCheck.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncPremiumCheck.java new file mode 100644 index 00000000..cb342da4 --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncPremiumCheck.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.task; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.JoinManagement; +import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; +import com.github.games647.fastlogin.velocity.FastLoginVelocity; +import com.github.games647.fastlogin.velocity.VelocityLoginSession; +import com.github.games647.fastlogin.velocity.VelocityLoginSource; +import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPreLoginEvent; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.Continuation; +import com.velocitypowered.api.event.connection.PreLoginEvent; +import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.Player; + +import java.util.concurrent.ExecutionException; + +public class AsyncPremiumCheck extends JoinManagement + implements Runnable { + + private final FastLoginVelocity plugin; + private final String username; + private Continuation continuation; + private PreLoginEvent preLoginEvent; + private final InboundConnection connection; + + public AsyncPremiumCheck(FastLoginVelocity plugin, InboundConnection connection, String username, Continuation continuation, PreLoginEvent preLoginEvent) { + super(plugin.getCore(), plugin.getCore().getAuthPluginHook()); + this.plugin = plugin; + this.connection = connection; + this.username = username; + this.continuation = continuation; + this.preLoginEvent = preLoginEvent; + } + + @Override + public void run() { + plugin.getSession().remove(connection.getRemoteAddress()); + try { + super.onLogin(username, new VelocityLoginSource(connection, preLoginEvent)); + } finally { + continuation.resume(); + } + } + + @Override + public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, VelocityLoginSource source, StoredProfile profile) { + VelocityFastLoginPreLoginEvent event = new VelocityFastLoginPreLoginEvent(username, source, profile); + try { + return plugin.getProxy().getEventManager().fire(event).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore the interrupt flag + return event; + } catch (ExecutionException e) { + core.getPlugin().getLog().error("Error firing event", e); + return event; + } + } + + @Override + public void requestPremiumLogin(VelocityLoginSource source, StoredProfile profile, + String username, boolean registered) { + source.enableOnlinemode(); + plugin.getSession().put(source.getConnection().getRemoteAddress(), new VelocityLoginSession(username, registered, profile)); + + String ip = source.getAddress().getAddress().getHostAddress(); + plugin.getCore().getPendingLogin().put(ip + username, new Object()); + } + + @Override + public void startCrackedSession(VelocityLoginSource source, StoredProfile profile, String username) { + plugin.getSession().put(source.getConnection().getRemoteAddress(), new VelocityLoginSession(username, false, profile)); + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java new file mode 100644 index 00000000..d1b24cf5 --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.task; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.shared.FastLoginCore; +import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason; +import com.github.games647.fastlogin.velocity.FastLoginVelocity; +import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPremiumToggleEvent; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +public class AsyncToggleMessage implements Runnable { + + private final FastLoginCore core; + private final CommandSource sender; + private final String senderName; + private final String targetPlayer; + private final boolean toPremium; + private final boolean isPlayerSender; + + public AsyncToggleMessage(FastLoginCore core, + CommandSource sender, String playerName, boolean toPremium, boolean playerSender) { + this.core = core; + this.sender = sender; + this.targetPlayer = playerName; + this.toPremium = toPremium; + this.isPlayerSender = playerSender; + if (sender instanceof Player) + senderName = ((Player) sender).getUsername(); + else + senderName = ""; + } + + @Override + public void run() { + if (toPremium) { + activatePremium(); + } else { + turnOffPremium(); + } + } + + private void turnOffPremium() { + StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer); + //existing player is already cracked + if (playerProfile.isSaved() && !playerProfile.isPremium()) { + sendMessage("not-premium"); + return; + } + + playerProfile.setPremium(false); + playerProfile.setId(null); + core.getStorage().save(playerProfile); + PremiumToggleReason reason = (!isPlayerSender || !senderName.equalsIgnoreCase(playerProfile.getName())) ? + PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF; + core.getPlugin().getProxy().getEventManager().fire( + new VelocityFastLoginPremiumToggleEvent(playerProfile, reason)); + sendMessage("remove-premium"); + } + + private void activatePremium() { + StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer); + if (playerProfile.isPremium()) { + sendMessage("already-exists"); + return; + } + + playerProfile.setPremium(true); + core.getStorage().save(playerProfile); + PremiumToggleReason reason = (!isPlayerSender || !senderName.equalsIgnoreCase(playerProfile.getName())) ? + PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF; + core.getPlugin().getProxy().getEventManager().fire( + new VelocityFastLoginPremiumToggleEvent(playerProfile, reason)); + sendMessage("add-premium"); + } + + private void sendMessage(String localeId) { + String message = core.getMessage(localeId); + if (isPlayerSender) { + sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + } else { + core.getPlugin().getProxy().getConsoleCommandSource().sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + } + } +} diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/ForceLoginTask.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/ForceLoginTask.java new file mode 100644 index 00000000..c648b24d --- /dev/null +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/ForceLoginTask.java @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.velocity.task; + +import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.message.ChannelMessage; +import com.github.games647.fastlogin.core.message.LoginActionMessage; +import com.github.games647.fastlogin.core.message.LoginActionMessage.Type; +import com.github.games647.fastlogin.core.shared.FastLoginCore; +import com.github.games647.fastlogin.core.shared.ForceLoginManagement; +import com.github.games647.fastlogin.core.shared.LoginSession; +import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; +import com.github.games647.fastlogin.velocity.FastLoginVelocity; +import com.github.games647.fastlogin.velocity.VelocityLoginSession; +import com.github.games647.fastlogin.velocity.event.VelocityFastLoginAutoLoginEvent; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ForceLoginTask + extends ForceLoginManagement { + + private final RegisteredServer server; + + //treat player as if they had a premium account, even when they don't + //used for Floodgate auto login/register + private final boolean forcedOnlineMode; + + public ForceLoginTask(FastLoginCore core, + Player player, RegisteredServer server, VelocityLoginSession session, boolean forcedOnlineMode) { + super(core, player, session); + + this.server = server; + this.forcedOnlineMode = forcedOnlineMode; + } + + public ForceLoginTask(FastLoginCore core, Player player, + RegisteredServer server, VelocityLoginSession session) { + this(core, player, server, session, false); + } + + @Override + public void run() { + if (session == null) { + return; + } + + super.run(); + if (!isOnlineMode()) { + session.setAlreadySaved(true); + } + } + + @Override + public boolean forceLogin(Player player) { + if (session.isAlreadyLogged()) { + return true; + } + + session.setAlreadyLogged(true); + return super.forceLogin(player); + } + + @Override + public FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) { + VelocityFastLoginAutoLoginEvent event = new VelocityFastLoginAutoLoginEvent(session, profile); + try { + return core.getPlugin().getProxy().getEventManager().fire(event).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Set the interrupt flag again + return event; + } catch (ExecutionException e) { + core.getPlugin().getLog().error("Error firing event", e); + return event; + } + } + + @Override + public boolean forceRegister(Player player) { + return session.isAlreadyLogged() || super.forceRegister(player); + } + + @Override + public void onForceActionSuccess(LoginSession session) { + //sub channel name + Type type = Type.LOGIN; + if (session.needsRegistration()) { + type = Type.REGISTER; + } + + UUID proxyId = core.getPlugin().getProxyId(); + ChannelMessage loginMessage = new LoginActionMessage(type, player.getUsername(), proxyId); + core.getPlugin().sendPluginMessage(server, loginMessage); + } + + @Override + public String getName(Player player) { + return player.getUsername(); + } + + @Override + public boolean isOnline(Player player) { + return player.isActive(); + } + + @Override + public boolean isOnlineMode() { + return forcedOnlineMode || player.isOnlineMode(); + } +}