forked from TuxCoding/FastLogin
Compare commits
29 Commits
main
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32197fb467 | ||
|
|
65a379c3c2 | ||
|
|
2ff6547552 | ||
|
|
c3233b2c67 | ||
|
|
f86c918a8b | ||
|
|
001cf3237b | ||
|
|
c28c634351 | ||
|
|
4550562465 | ||
|
|
2308d98dcd | ||
|
|
063b6a9405 | ||
|
|
1b45b8d2be | ||
|
|
8be0de4781 | ||
|
|
d06c44041d | ||
|
|
69fa341188 | ||
|
|
e1ba698f28 | ||
|
|
7bf613c8b4 | ||
|
|
1c194280b1 | ||
|
|
5803a34f84 | ||
|
|
86c4f48ec6 | ||
|
|
06e6741493 | ||
|
|
98da5a36da | ||
|
|
3bd9e60783 | ||
|
|
568ad7a621 | ||
|
|
1464ef2952 | ||
|
|
46239c9ffc | ||
|
|
083068b856 | ||
|
|
dd2aa922d7 | ||
|
|
b77ea285e5 | ||
|
|
f8f0e7ac7a |
25
.github/release.yml
vendored
Normal file
25
.github/release.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Configure how the release notes are generated for GitHub releases
|
||||
|
||||
changelog:
|
||||
# List of authors (like bots) and labels to exclude from pull requests
|
||||
exclude:
|
||||
labels:
|
||||
- ignore-for-release
|
||||
categories:
|
||||
- title: 🛠 Breaking Changes
|
||||
labels:
|
||||
- Semver-Major
|
||||
- breaking-change
|
||||
- title: 🎉 Exciting New Features
|
||||
labels:
|
||||
- Semver-Minor
|
||||
- enhancement
|
||||
- title: 🐞 Bugfixes
|
||||
labels:
|
||||
- bug
|
||||
- title: 👒 Dependencies
|
||||
labels:
|
||||
- dependencies
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- "*"
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
@@ -59,6 +59,6 @@ jobs:
|
||||
run: mvn package -f "pom.xml" --batch-mode -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true -t /home/runner/.m2/toolchains.xml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
2
.github/workflows/maven.yml
vendored
2
.github/workflows/maven.yml
vendored
@@ -64,4 +64,4 @@ jobs:
|
||||
|
||||
- name: Submit Dependency Snapshot
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: advanced-security/maven-dependency-submission-action@v4.0.3
|
||||
uses: advanced-security/maven-dependency-submission-action@v5.0.0
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# FastLogin
|
||||
|
||||

|
||||
|
||||
Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
|
||||
So they don't need to enter passwords. This is also called auto login (auto-login).
|
||||
|
||||
@@ -30,7 +32,11 @@ Development builds contain the latest changes from the Source-Code. They are ble
|
||||
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
|
||||
side on `Changes`, you can see iterative change sets leading to a specific build.
|
||||
|
||||
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
|
||||
~~You can download them from here: [CodeMC(Jenkins)](https://ci.codemc.org/job/Games647/job/FastLogin/)~~
|
||||
|
||||
Currently broken due changed usernames. Download it from [here](https://github.com/TuxCoding/FastLogin/releases)
|
||||
|
||||
|
||||
|
||||
***
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.20.6-R0.1-SNAPSHOT</version>
|
||||
<version>1.21.6-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Use our own newer api version -->
|
||||
<exclusions>
|
||||
@@ -348,6 +348,22 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.zigazajc007</groupId>
|
||||
<artifactId>Passky</artifactId>
|
||||
<version>v3.0.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
|
||||
<!-- Exclude dependencies to prevent potential version conflicts-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--No maven repository :(-->
|
||||
<dependency>
|
||||
<groupId>de.st_ddt.crazy</groupId>
|
||||
@@ -379,7 +395,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
<version>1.80</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import io.papermc.paper.configuration.ServerConfiguration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -133,6 +134,13 @@ public class BungeeManager {
|
||||
}
|
||||
|
||||
private boolean detectProxy() {
|
||||
try {
|
||||
ServerConfiguration.class.getDeclaredMethod("isProxyEnabled");
|
||||
return Bukkit.getServerConfig().isProxyEnabled();
|
||||
} catch (NoClassDefFoundError | NoSuchMethodException noSuchClassMethodEx) {
|
||||
// Ignore continue below
|
||||
}
|
||||
|
||||
try {
|
||||
if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) {
|
||||
return true;
|
||||
|
||||
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bukkit;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.DeleteCommand;
|
||||
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
||||
@@ -155,6 +156,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
//register commands using a unique name
|
||||
Optional.ofNullable(getCommand("premium")).ifPresent(c -> c.setExecutor(new PremiumCommand(this)));
|
||||
Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this)));
|
||||
Optional.ofNullable(getCommand("fldelete")).ifPresent(c -> c.setExecutor(new DeleteCommand(this)));
|
||||
}
|
||||
|
||||
private boolean initializeFloodgate() {
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEv
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
@@ -57,6 +58,7 @@ public class CrackedCommand extends ToggleCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
if (forwardCrackedCommand(sender, sender.getName())) {
|
||||
return;
|
||||
}
|
||||
@@ -71,7 +73,16 @@ public class CrackedCommand extends ToggleCommand {
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER)
|
||||
);
|
||||
|
||||
plugin.getScheduler().getSyncExecutor().execute(() -> {
|
||||
if (plugin.getCore().getConfig().getBoolean("kick-toggle", true)) {
|
||||
player.kickPlayer(plugin.getCore().getMessage("remove-premium"));
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("add-premium", sender);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
||||
@@ -104,7 +115,7 @@ public class CrackedCommand extends ToggleCommand {
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
*
|
||||
* 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.bukkit.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
|
||||
public class DeleteCommand implements TabExecutor {
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public DeleteCommand(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the command to delete profiles.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
|
||||
if (!sender.hasPermission(command.getPermission())) {
|
||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (plugin.getBungeeManager().isEnabled()) {
|
||||
sender.sendMessage("Error: Cannot delete profile entries when using BungeeCord!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("Error: Must supply username to delete!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getCore().getStorage().deleteProfile(args[0]);
|
||||
if (!(sender instanceof ConsoleCommandSender)) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (count == 0) {
|
||||
sender.sendMessage("Error: No profile entries found!");
|
||||
} else {
|
||||
sender.sendMessage("Deleted " + count + " matching profile entries");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.getName().toLowerCase().startsWith(args[0])) {
|
||||
list.add(p.getName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,8 @@ public class PremiumCommand extends ToggleCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID id = ((Player) sender).getUniqueId();
|
||||
Player player = (Player) sender;
|
||||
UUID id = player.getUniqueId();
|
||||
if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getCore().getPendingConfirms().contains(id)) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("premium-warning"));
|
||||
plugin.getCore().getPendingConfirms().add(id);
|
||||
@@ -86,10 +87,17 @@ public class PremiumCommand extends ToggleCommand {
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_SELF));
|
||||
});
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_SELF)
|
||||
);
|
||||
|
||||
plugin.getCore().sendLocaleMessage("add-premium", sender);
|
||||
plugin.getScheduler().getSyncExecutor().execute(() -> {
|
||||
if (plugin.getCore().getConfig().getBoolean("kick-toggle", true)) {
|
||||
player.kickPlayer(plugin.getCore().getMessage("remove-premium"));
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("add-premium", sender);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +125,8 @@ public class PremiumCommand extends ToggleCommand {
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER)
|
||||
);
|
||||
});
|
||||
|
||||
plugin.getCore().sendLocaleMessage("add-premium-other", sender);
|
||||
|
||||
@@ -27,6 +27,7 @@ package com.github.games647.fastlogin.bukkit.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -34,11 +35,15 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final CommandSender invoker;
|
||||
private final StoredProfile profile;
|
||||
private final PremiumToggleReason reason;
|
||||
|
||||
public BukkitFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
|
||||
public BukkitFastLoginPremiumToggleEvent(CommandSender invoker, StoredProfile profile, PremiumToggleReason reason) {
|
||||
super(true);
|
||||
|
||||
this.invoker = invoker;
|
||||
this.profile = profile;
|
||||
this.reason = reason;
|
||||
}
|
||||
@@ -48,6 +53,13 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return who triggered this change. This could be a Player for itself or others (Admin) or the console.
|
||||
*/
|
||||
public CommandSender getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PremiumToggleReason getReason() {
|
||||
return reason;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
*
|
||||
* 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.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.rabbitcomapny.api.Identifier;
|
||||
import com.rabbitcomapny.api.LoginResult;
|
||||
import com.rabbitcomapny.api.PasskyAPI;
|
||||
import com.rabbitcomapny.api.RegisterResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PasskyHook implements AuthPlugin<Player> {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public PasskyHook(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
LoginResult result = PasskyAPI.forceLogin(new Identifier(player), true);
|
||||
if (!result.success) {
|
||||
plugin.getLog().error("Failed to force login {} via Passky: {}", player.getName(), result.status);
|
||||
}
|
||||
|
||||
return result.success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
RegisterResult result = PasskyAPI.forceRegister(new Identifier(player), password, true);
|
||||
if (!result.success) {
|
||||
plugin.getLog().error("Failed to register {} via Passky: {}", player.getName(), result.status);
|
||||
}
|
||||
|
||||
return result.success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) {
|
||||
return PasskyAPI.isRegistered(new Identifier(playerName));
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.primitives.Longs;
|
||||
import lombok.val;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@@ -41,6 +40,7 @@ import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
@@ -53,6 +53,7 @@ import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
@@ -198,9 +199,9 @@ final class EncryptionUtil {
|
||||
|
||||
private static PublicKey loadMojangSessionKey()
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
|
||||
val keyData = Resources.toByteArray(keyUrl);
|
||||
val keySpec = new X509EncodedKeySpec(keyData);
|
||||
URL keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
|
||||
byte[] keyData = Resources.toByteArray(keyUrl);
|
||||
KeySpec keySpec = new X509EncodedKeySpec(keyData);
|
||||
|
||||
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
@@ -48,7 +49,6 @@ import com.mojang.datafixers.util.Either;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.val;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -245,8 +245,9 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
// public key is sent separate
|
||||
clientKey = Optional.empty();
|
||||
} else {
|
||||
val profileKey = packet.getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter())
|
||||
.optionRead(0);
|
||||
Optional<Optional<WrappedProfileKeyData>> profileKey = packet.getOptionals(
|
||||
BukkitConverters.getWrappedPublicKeyDataConverter()
|
||||
).optionRead(0);
|
||||
|
||||
clientKey = profileKey.flatMap(Function.identity()).flatMap(data -> {
|
||||
Instant expires = data.getExpireTime();
|
||||
|
||||
@@ -50,7 +50,6 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.InetUtils;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import lombok.val;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
@@ -307,7 +306,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
startPacket.getStrings().write(0, username);
|
||||
|
||||
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
|
||||
val wrappedKey = Optional.ofNullable(clientKey).map(key ->
|
||||
Optional<WrappedProfileKeyData> wrappedKey = Optional.ofNullable(clientKey).map(key ->
|
||||
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
|
||||
);
|
||||
|
||||
|
||||
@@ -25,20 +25,38 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@Value(staticConstructor = "of")
|
||||
public class ClientPublicKey {
|
||||
Instant expiry;
|
||||
PublicKey key;
|
||||
byte[] signature;
|
||||
|
||||
private final Instant expiry;
|
||||
private final PublicKey key;
|
||||
private final byte[] signature;
|
||||
|
||||
public Instant expiry() {
|
||||
return expiry;
|
||||
}
|
||||
|
||||
public PublicKey key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public byte[] signature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
|
||||
this.expiry = expiry;
|
||||
this.key = key;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public static ClientPublicKey of(Instant expiry, PublicKey key, byte[] signature) {
|
||||
return new ClientPublicKey(expiry, key, signature);
|
||||
}
|
||||
|
||||
public boolean isExpired(Instant verifyTimestamp) {
|
||||
return !verifyTimestamp.isBefore(expiry);
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.github.games647.fastlogin.bukkit.hook.LogItHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.XAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.PasskyHook;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -94,7 +95,7 @@ public class DelayedAuthHook implements Runnable {
|
||||
try {
|
||||
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
|
||||
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class,
|
||||
XAuthHook.class);
|
||||
XAuthHook.class, PasskyHook.class);
|
||||
|
||||
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
|
||||
String pluginName = clazz.getSimpleName();
|
||||
|
||||
@@ -30,6 +30,7 @@ softdepend:
|
||||
- LogIt
|
||||
- UltraAuth
|
||||
- xAuth
|
||||
- Passky
|
||||
|
||||
commands:
|
||||
${project.parent.name}:
|
||||
@@ -44,6 +45,11 @@ commands:
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.cracked
|
||||
|
||||
fldelete:
|
||||
description: 'Delete player profile data'
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.delete
|
||||
|
||||
permissions:
|
||||
${project.artifactId}.command.premium:
|
||||
description: 'Label themselves as premium'
|
||||
@@ -62,3 +68,7 @@ permissions:
|
||||
description: 'Label others as cracked'
|
||||
children:
|
||||
${project.artifactId}.command.cracked: true
|
||||
|
||||
${project.artifactId}.command.delete:
|
||||
description: 'Delete other players profile data'
|
||||
default: op
|
||||
@@ -26,7 +26,7 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -37,13 +37,13 @@ class FastLoginBukkitTest {
|
||||
|
||||
@Test
|
||||
void testRGB() {
|
||||
val message = "&x00002a00002b&lText";
|
||||
val msg = CommonUtil.translateColorCodes(message);
|
||||
String message = "&x00002a00002b&lText";
|
||||
String msg = CommonUtil.translateColorCodes(message);
|
||||
assertEquals(msg, "§x00002a00002b§lText");
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
val components = TextComponent.fromLegacyText(msg);
|
||||
val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
|
||||
BaseComponent[] components = TextComponent.fromLegacyText(msg);
|
||||
String expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
|
||||
//noinspection deprecation
|
||||
assertEquals(ComponentSerializer.toString(components), expected);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
@@ -37,7 +36,7 @@ public class Base64Adapter extends TypeAdapter<byte[]> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, byte[] value) throws IOException {
|
||||
val encoded = Base64.getEncoder().encodeToString(value);
|
||||
String encoded = Base64.getEncoder().encodeToString(value);
|
||||
out.value(encoded);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import lombok.val;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
@@ -51,6 +51,7 @@ import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@@ -60,7 +61,7 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testVerifyToken() {
|
||||
val random = ThreadLocalRandom.current();
|
||||
Random random = ThreadLocalRandom.current();
|
||||
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
assertAll(
|
||||
@@ -88,10 +89,10 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testExpiredClientKey() throws Exception {
|
||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
|
||||
// Client expires at the exact second mentioned, so use it for verification
|
||||
val expiredTimestamp = clientKey.expiry();
|
||||
Instant expiredTimestamp = clientKey.expiry();
|
||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp, null));
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ class EncryptionUtilTest {
|
||||
"client_keys/invalid_wrong_signature.json"
|
||||
})
|
||||
void testInvalidClientKey(String clientKeySource) throws Exception {
|
||||
val clientKey = ResourceLoader.loadClientKey(clientKeySource);
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey(clientKeySource);
|
||||
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp, null));
|
||||
@@ -113,34 +114,34 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testValidClientKey() throws Exception {
|
||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValid191ClientKey() throws Exception {
|
||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
||||
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
val ownerPremiumId = UUID.fromString("0aaa2c13-922a-411b-b655-9b8c08404695");
|
||||
UUID ownerPremiumId = UUID.fromString("0aaa2c13-922a-411b-b655-9b8c08404695");
|
||||
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncorrect191ClientOwner() throws Exception {
|
||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
||||
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
val ownerPremiumId = UUID.fromString("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6");
|
||||
UUID ownerPremiumId = UUID.fromString("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6");
|
||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptSharedSecret() throws Exception {
|
||||
KeyPair serverPair = EncryptionUtil.generateKeyPair();
|
||||
val serverPK = serverPair.getPublic();
|
||||
PublicKey serverPK = serverPair.getPublic();
|
||||
|
||||
SecretKey secretKey = generateSharedKey();
|
||||
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
|
||||
@@ -152,7 +153,7 @@ class EncryptionUtilTest {
|
||||
private static byte[] encrypt(PublicKey receiverKey, byte... message)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
IllegalBlockSizeException, BadPaddingException {
|
||||
val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
|
||||
Cipher encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
|
||||
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
|
||||
return encryptCipher.doFinal(message);
|
||||
}
|
||||
@@ -168,9 +169,9 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testServerIdHash() throws Exception {
|
||||
val serverId = "";
|
||||
val sharedSecret = generateSharedKey();
|
||||
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
|
||||
@@ -185,7 +186,7 @@ class EncryptionUtilTest {
|
||||
// sha1.update(server's encoded public key from Encryption Request)
|
||||
// hash := sha1.hexdigest() # String of hex characters
|
||||
@SuppressWarnings("deprecation")
|
||||
val hasher = Hashing.sha1().newHasher();
|
||||
Hasher hasher = Hashing.sha1().newHasher();
|
||||
hasher.putString(serverId, StandardCharsets.US_ASCII);
|
||||
hasher.putBytes(sharedSecret.getEncoded());
|
||||
hasher.putBytes(serverPK.getEncoded());
|
||||
@@ -198,9 +199,9 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testServerIdHashWrongSecret() throws Exception {
|
||||
val serverId = "";
|
||||
val sharedSecret = generateSharedKey();
|
||||
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
|
||||
@@ -208,12 +209,12 @@ class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testServerIdHashWrongServerKey() {
|
||||
val serverId = "";
|
||||
val sharedSecret = generateSharedKey();
|
||||
val serverPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
PublicKey wrongPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
|
||||
}
|
||||
|
||||
@@ -257,8 +258,8 @@ class EncryptionUtilTest {
|
||||
@Test
|
||||
void testNonce() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
val serverKey = EncryptionUtil.generateKeyPair();
|
||||
val encryptedNonce = encrypt(serverKey.getPublic(), expected);
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
byte[] encryptedNonce = encrypt(serverKey.getPublic(), expected);
|
||||
|
||||
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
||||
}
|
||||
@@ -266,19 +267,19 @@ class EncryptionUtilTest {
|
||||
@Test
|
||||
void testNonceIncorrect() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
val serverKey = EncryptionUtil.generateKeyPair();
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
|
||||
// flipped first character
|
||||
val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
|
||||
byte[] encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
|
||||
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonceFailedDecryption() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
val serverKey = EncryptionUtil.generateKeyPair();
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
// generate a new keypair that is different
|
||||
val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
|
||||
byte[] encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
|
||||
|
||||
assertThrows(GeneralSecurityException.class,
|
||||
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
|
||||
@@ -288,7 +289,7 @@ class EncryptionUtilTest {
|
||||
@Test
|
||||
void testNonceIncorrectEmpty() {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
val serverKey = EncryptionUtil.generateKeyPair();
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
byte[] encryptedNonce = {};
|
||||
|
||||
assertThrows(GeneralSecurityException.class,
|
||||
|
||||
@@ -28,16 +28,16 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SignatureTestData {
|
||||
|
||||
public static SignatureTestData fromResource(String resourceName) throws IOException {
|
||||
val keyUrl = Resources.getResource(resourceName);
|
||||
val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
||||
URL keyUrl = Resources.getResource(resourceName);
|
||||
String encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
||||
|
||||
return new Gson().fromJson(encodedSignature, SignatureTestData.class);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
package com.github.games647.fastlogin.bukkit.task;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import lombok.val;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -36,7 +35,7 @@ class DelayedAuthHookTest {
|
||||
|
||||
@Test
|
||||
void createNewReflectiveInstance() throws ReflectiveOperationException {
|
||||
val authHook = new DelayedAuthHook(null);
|
||||
DelayedAuthHook authHook = new DelayedAuthHook(null);
|
||||
assertNotNull(authHook.newInstance(DummyHook.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,12 @@ public class AsyncToggleMessage implements Runnable {
|
||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getPluginManager().callEvent(
|
||||
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
sendMessage("remove-premium");
|
||||
|
||||
if (isPlayerSender && core.getConfig().getBoolean("kick-toggle", true)) {
|
||||
sender.disconnect(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
|
||||
} else {
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
}
|
||||
|
||||
private void activatePremium() {
|
||||
|
||||
@@ -140,21 +140,21 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>2.0.13</version>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
<!-- snakeyaml is present in Bungee, Spigot, so we could use this independent implementation -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.20-R0.2-SNAPSHOT</version>
|
||||
<version>1.20-R0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- This is optional in BungeeCord-config, so we include it here manually -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
@@ -203,7 +203,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>craftapi</artifactId>
|
||||
<version>0.8.1</version>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Database driver included in Spigot -->
|
||||
|
||||
@@ -27,10 +27,9 @@ package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.craftapi.model.auth.Verification;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.craftapi.resolver.Options;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -44,38 +43,12 @@ import java.util.Optional;
|
||||
*/
|
||||
public class ProxyAgnosticMojangResolver extends MojangResolver {
|
||||
|
||||
private static final String HOST = "sessionserver.mojang.com";
|
||||
|
||||
/**
|
||||
* A formatting string containing a URL used to call the {@code hasJoined} method on mojang session servers.
|
||||
* <p>
|
||||
* Formatting parameters:
|
||||
* 1. The username of the player in question
|
||||
* 2. The serverId of this server
|
||||
*/
|
||||
public static final String ENDPOINT = "https://" + HOST + "/session/minecraft/hasJoined?username=%s&serverId=%s";
|
||||
|
||||
public ProxyAgnosticMojangResolver(Options options) {
|
||||
super(options);
|
||||
}
|
||||
@Override
|
||||
public Optional<Verification> hasJoined(String username, String serverHash, InetAddress hostIp)
|
||||
throws IOException {
|
||||
String url = String.format(ENDPOINT, username, serverHash);
|
||||
|
||||
HttpURLConnection conn = this.getConnection(url);
|
||||
int responseCode = conn.getResponseCode();
|
||||
|
||||
Verification verification = null;
|
||||
|
||||
// Mojang session servers send HTTP 204 (NO CONTENT) when the authentication seems invalid
|
||||
// If that's not our case, the authentication is valid, and so we can parse the response.
|
||||
if (responseCode != HttpURLConnection.HTTP_NO_CONTENT) {
|
||||
verification = this.parseRequest(conn, this::parseVerification);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(verification);
|
||||
}
|
||||
|
||||
// Functional implementation of InputStreamAction, used in hasJoined method in parseRequest call
|
||||
protected Verification parseVerification(InputStream input) throws IOException {
|
||||
return this.readJson(input, Verification.class);
|
||||
return super.hasJoined(username, serverHash, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class LoginActionMessage implements ChannelMessage {
|
||||
|
||||
@Override
|
||||
public void readFrom(ByteArrayDataInput input) {
|
||||
this.type = Type.values()[input.readInt()];
|
||||
this.type = Type.values()[input.readByte()];
|
||||
|
||||
this.playerName = input.readUTF();
|
||||
|
||||
@@ -75,7 +75,7 @@ public class LoginActionMessage implements ChannelMessage {
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteArrayDataOutput output) {
|
||||
output.writeInt(type.ordinal());
|
||||
output.writeByte(type.ordinal());
|
||||
|
||||
//Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
|
||||
output.writeUTF(playerName);
|
||||
|
||||
@@ -43,9 +43,30 @@ public abstract class AbstractAsyncScheduler {
|
||||
this.processingPool = processingPool;
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<Void> runAsync(Runnable task);
|
||||
public CompletableFuture<Void> runAsync(Runnable task) {
|
||||
return CompletableFuture.runAsync(() -> process(task), processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<Void> runAsyncDelayed(Runnable task, Duration delay);
|
||||
public CompletableFuture<Void> runAsyncDelayed(Runnable task, Duration delay) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
currentlyRunning.incrementAndGet();
|
||||
try {
|
||||
Thread.sleep(delay.toMillis());
|
||||
task.run();
|
||||
} catch (InterruptedException interruptedException) {
|
||||
// restore interrupt flag
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
currentlyRunning.getAndDecrement();
|
||||
}
|
||||
}, processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected void process(Runnable task) {
|
||||
currentlyRunning.incrementAndGet();
|
||||
|
||||
@@ -27,8 +27,6 @@ package com.github.games647.fastlogin.core.scheduler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -42,33 +40,8 @@ public class AsyncScheduler extends AbstractAsyncScheduler {
|
||||
|
||||
public AsyncScheduler(Logger logger, Executor processingPool) {
|
||||
super(logger, processingPool);
|
||||
logger.info("Using legacy scheduler");
|
||||
logger.info("Using legacy platform scheduler for using an older Java version. "
|
||||
+ "Upgrade Java to 21+ for improved performance");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> runAsync(Runnable task) {
|
||||
return CompletableFuture.runAsync(() -> process(task), processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> runAsyncDelayed(Runnable task, Duration delay) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
currentlyRunning.incrementAndGet();
|
||||
try {
|
||||
Thread.sleep(delay.toMillis());
|
||||
process(task);
|
||||
} catch (InterruptedException interruptedException) {
|
||||
// restore interrupt flag
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
currentlyRunning.getAndDecrement();
|
||||
}
|
||||
}, processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.craftapi.resolver.Options;
|
||||
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.ProxyAgnosticMojangResolver;
|
||||
@@ -48,11 +49,9 @@ import net.md_5.bungee.config.YamlConfiguration;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.Proxy.Type;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
@@ -121,30 +120,35 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the resolver based on the config parameter
|
||||
this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false)
|
||||
? new ProxyAgnosticMojangResolver() : new MojangResolver();
|
||||
Options resolverOptions = new Options();
|
||||
resolverOptions.setMaxNameRequests(config.getInt("mojang-request-limit", 600));
|
||||
|
||||
antiBot = createAntiBotService(config.getSection("anti-bot"));
|
||||
Set<Proxy> proxies = config.getStringList("proxies")
|
||||
.stream()
|
||||
.map(proxy -> proxy.split(":"))
|
||||
.map(proxy -> new InetSocketAddress(proxy[0], Integer.parseInt(proxy[1])))
|
||||
.map(sa -> new Proxy(Type.HTTP, sa))
|
||||
.collect(toSet());
|
||||
|
||||
Collection<InetAddress> addresses = new HashSet<>();
|
||||
for (String localAddress : config.getStringList("ip-addresses")) {
|
||||
try {
|
||||
addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
|
||||
} catch (UnknownHostException ex) {
|
||||
plugin.getLog().error("IP-Address is unknown to us", ex);
|
||||
}
|
||||
if (!proxies.isEmpty()) {
|
||||
resolverOptions.setProxySelector(new RotatingProxySelector(proxies));
|
||||
}
|
||||
|
||||
resolver.setMaxNameRequests(config.getInt("mojang-request-limit"));
|
||||
resolver.setProxySelector(new RotatingProxySelector(proxies));
|
||||
resolver.setOutgoingAddresses(addresses);
|
||||
// TODO: Not available currently in craftapi?
|
||||
// Collection<InetAddress> addresses = new HashSet<>();
|
||||
// for (String localAddress : config.getStringList("ip-addresses")) {
|
||||
// try {
|
||||
// addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
|
||||
// } catch (UnknownHostException ex) {
|
||||
// plugin.getLog().error("IP-Address is unknown to us", ex);
|
||||
// }
|
||||
// }
|
||||
// resolver.setOutgoingAddresses(addresses);
|
||||
|
||||
// Initialize the resolver based on the config parameter
|
||||
this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false)
|
||||
? new ProxyAgnosticMojangResolver(resolverOptions) : new MojangResolver(resolverOptions);
|
||||
|
||||
antiBot = createAntiBotService(config.getSection("anti-bot"));
|
||||
}
|
||||
|
||||
private AntiBotService createAntiBotService(Configuration botSection) {
|
||||
|
||||
@@ -32,6 +32,8 @@ public interface AuthStorage {
|
||||
|
||||
StoredProfile loadProfile(UUID uuid);
|
||||
|
||||
int deleteProfile(String name);
|
||||
|
||||
void save(StoredProfile playerProfile);
|
||||
|
||||
void close();
|
||||
|
||||
@@ -65,6 +65,8 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
+ "` WHERE `Name`=? LIMIT 1";
|
||||
protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE
|
||||
+ "` WHERE `UUID`=? LIMIT 1";
|
||||
protected static final String DELETE_BY_NAME = "DELETE FROM " + PREMIUM_TABLE
|
||||
+ " WHERE `Name` = ?";
|
||||
protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
|
||||
+ "` (`UUID`, `Name`, `Premium`, `Floodgate`, `LastIp`) " + "VALUES (?, ?, ?, ?, ?) ";
|
||||
// limit not necessary here, because it's unique
|
||||
@@ -143,6 +145,25 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteProfile(String name) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement deleteStmt = con.prepareStatement(DELETE_BY_NAME)) {
|
||||
deleteStmt.setString(1, name);
|
||||
|
||||
int rowsDeleted = deleteStmt.executeUpdate();
|
||||
if (rowsDeleted > 0) {
|
||||
log.info("Deleted {}'s profile data", name);
|
||||
} else {
|
||||
log.info("No profile data found for {}", name);
|
||||
}
|
||||
return rowsDeleted;
|
||||
} catch (SQLException sqlEx) {
|
||||
log.error("Failed to query profile: {}", name, sqlEx);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<StoredProfile> parseResult(ResultSet resultSet) throws SQLException {
|
||||
if (resultSet.next()) {
|
||||
long userId = resultSet.getInt("UserID");
|
||||
|
||||
@@ -27,8 +27,6 @@ package com.github.games647.fastlogin.core.scheduler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -46,33 +44,4 @@ public class AsyncScheduler extends AbstractAsyncScheduler {
|
||||
|
||||
logger.info("Using optimized green threads with Java 21");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> runAsync(Runnable task) {
|
||||
return CompletableFuture
|
||||
.runAsync(() -> process(task), processingPool)
|
||||
.exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> runAsyncDelayed(Runnable task, Duration delay) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
currentlyRunning.incrementAndGet();
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
process(task);
|
||||
} catch (InterruptedException interruptedException) {
|
||||
// restore interrupt flag
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
currentlyRunning.getAndDecrement();
|
||||
}
|
||||
}, processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +150,14 @@ nameChangeCheck: false
|
||||
forwardSkin: true
|
||||
|
||||
# Displays a warning message that this message SHOULD only be invoked by
|
||||
# users who actually are the owner of this account. So not by cracked players
|
||||
# users who actually are the owner of this account (and not cracked players)
|
||||
#
|
||||
# If they still want to invoke the command, they have to invoke /premium again
|
||||
premium-warning: true
|
||||
|
||||
# Kick players after they confirmed the command from above.
|
||||
kick-toggle: true
|
||||
|
||||
# ======[[ Spigot+ProtocolLib users only ]]======
|
||||
# When set to true, enables the use of alternative session resolver which does not send the server IP
|
||||
# to mojang session servers. This setting might be useful when you are trying to run the server via a
|
||||
|
||||
22
pom.xml
22
pom.xml
@@ -83,7 +83,7 @@
|
||||
<plugin>
|
||||
<!-- Update compiler plugin for old Maven versions like GH runner -->
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.14.0</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
@@ -106,7 +106,7 @@
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>4.5</version>
|
||||
<version>5.0.0</version>
|
||||
<configuration>
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
@@ -134,7 +134,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
@@ -145,7 +145,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.17.0</version>
|
||||
<version>10.23.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
@@ -162,7 +162,7 @@
|
||||
<!-- Require newer versions for Junit5 support -->
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.3</version>
|
||||
<configuration>
|
||||
<!-- Work-around to make multi-release classes discoverable
|
||||
https://issues.apache.org/jira/browse/SUREFIRE-1731 -->
|
||||
@@ -218,18 +218,10 @@
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<!-- Use lombok to use some newer Java syntax features in Java 8 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombook.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.3</version>
|
||||
<version>5.12.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -237,7 +229,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.12.0</version>
|
||||
<version>5.17.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -62,6 +62,20 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<annotationProcessors>
|
||||
<annotationProcessor>
|
||||
com.velocitypowered.api.plugin.ap.PluginAnnotationProcessor
|
||||
</annotationProcessor>
|
||||
</annotationProcessors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
@@ -161,25 +175,15 @@
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<version>3.4.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.moandjiezana.toml</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Velocity does not ship any database driver-->
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.3</version>
|
||||
<exclusions>
|
||||
<!-- Exclude JNA implementation for WAFFLE - Windows Authentication Framework (mariadb)-->
|
||||
<exclusion>
|
||||
|
||||
@@ -66,4 +66,8 @@ public class VelocityLoginSource implements LoginSource {
|
||||
public InetSocketAddress getAddress() {
|
||||
return connection.getRemoteAddress();
|
||||
}
|
||||
|
||||
public InboundConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ConnectListener {
|
||||
if (event.isOnlineMode()) {
|
||||
LoginSession session = plugin.getSession().get(event.getConnection().getRemoteAddress());
|
||||
if (session == null) {
|
||||
plugin.getLog().warn("No active login session found for player {}", event.getUsername());
|
||||
plugin.getLog().error("No active login session found for onlinemode player {}", event.getUsername());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,6 +192,8 @@ public class ConnectListener {
|
||||
public void onDisconnect(DisconnectEvent disconnectEvent) {
|
||||
Player player = disconnectEvent.getPlayer();
|
||||
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
||||
|
||||
plugin.getSession().remove(player.getRemoteAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,12 +83,12 @@ public class PluginMessageListener {
|
||||
plugin.getScheduler().runAsync(() -> readMessage(forPlayer, channel, data));
|
||||
}
|
||||
|
||||
private void readMessage(Player forPlayer, String channel, byte[] data) {
|
||||
private void readMessage(Player sender, String channel, byte[] data) {
|
||||
FastLoginCore<Player, CommandSource, FastLoginVelocity> core = plugin.getCore();
|
||||
|
||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
|
||||
if (successChannel.equals(channel)) {
|
||||
onSuccessMessage(forPlayer);
|
||||
onSuccessMessage(sender);
|
||||
} else if (changeChannel.equals(channel)) {
|
||||
ChangePremiumMessage changeMessage = new ChangePremiumMessage();
|
||||
changeMessage.readFrom(dataInput);
|
||||
@@ -97,19 +97,19 @@ public class PluginMessageListener {
|
||||
boolean isSourceInvoker = changeMessage.isSourceInvoker();
|
||||
if (changeMessage.shouldEnable()) {
|
||||
Boolean premiumWarning = plugin.getCore().getConfig().get("premium-warning", true);
|
||||
if (playerName.equals(forPlayer.getUsername()) && premiumWarning
|
||||
&& !core.getPendingConfirms().contains(forPlayer.getUniqueId())) {
|
||||
if (playerName.equals(sender.getUsername()) && premiumWarning
|
||||
&& !core.getPendingConfirms().contains(sender.getUniqueId())) {
|
||||
String message = core.getMessage("premium-warning");
|
||||
forPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
core.getPendingConfirms().add(forPlayer.getUniqueId());
|
||||
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
core.getPendingConfirms().add(sender.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
core.getPendingConfirms().remove(forPlayer.getUniqueId());
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isSourceInvoker);
|
||||
core.getPendingConfirms().remove(sender.getUniqueId());
|
||||
Runnable task = new AsyncToggleMessage(core, sender, playerName, true, isSourceInvoker);
|
||||
plugin.getScheduler().runAsync(task);
|
||||
} else {
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isSourceInvoker);
|
||||
Runnable task = new AsyncToggleMessage(core, sender, playerName, false, isSourceInvoker);
|
||||
plugin.getScheduler().runAsync(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,8 @@ public class AsyncPremiumCheck extends JoinManagement<Player, CommandSource, Vel
|
||||
String username, boolean registered) {
|
||||
source.enableOnlinemode();
|
||||
VelocityLoginSession session = new VelocityLoginSession(username, registered, profile);
|
||||
plugin.getSession().put(source.getAddress(), session);
|
||||
plugin.getLog().error("Putting session: {}", source.getConnection());
|
||||
plugin.getSession().put(source.getConnection().getRemoteAddress(), session);
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().addLoginAttempt(ip, username);
|
||||
@@ -91,6 +92,6 @@ public class AsyncPremiumCheck extends JoinManagement<Player, CommandSource, Vel
|
||||
@Override
|
||||
public void startCrackedSession(VelocityLoginSource source, StoredProfile profile, String username) {
|
||||
VelocityLoginSession session = new VelocityLoginSession(username, false, profile);
|
||||
plugin.getSession().put(source.getAddress(), session);
|
||||
plugin.getSession().put(source.getConnection().getRemoteAddress(), session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,29 +33,26 @@ import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPremiumTogg
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public class AsyncToggleMessage implements Runnable {
|
||||
|
||||
private final FastLoginCore<Player, CommandSource, FastLoginVelocity> core;
|
||||
private final CommandSource sender;
|
||||
private final Player sender;
|
||||
private final String senderName;
|
||||
private final String targetPlayer;
|
||||
private final boolean toPremium;
|
||||
private final boolean isPlayerSender;
|
||||
|
||||
public AsyncToggleMessage(FastLoginCore<Player, CommandSource, FastLoginVelocity> core,
|
||||
CommandSource sender, String playerName, boolean toPremium, boolean playerSender) {
|
||||
Player 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 playSender) {
|
||||
senderName = playSender.getUsername();
|
||||
} else {
|
||||
senderName = "";
|
||||
}
|
||||
this.senderName = sender.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,7 +79,14 @@ public class AsyncToggleMessage implements Runnable {
|
||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getEventManager().fire(
|
||||
new VelocityFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
sendMessage("remove-premium");
|
||||
|
||||
if (isPlayerSender && core.getConfig().getBoolean("kick-toggle", true)) {
|
||||
TextComponent msg = LegacyComponentSerializer.legacyAmpersand()
|
||||
.deserialize(core.getMessage("remove-premium"));
|
||||
sender.disconnect(msg);
|
||||
} else {
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
}
|
||||
|
||||
private void activatePremium() {
|
||||
|
||||
Reference in New Issue
Block a user