Implementing #23
This commit is contained in:
parent
e92fc4c9a8
commit
82537de884
|
@ -22,6 +22,7 @@ import java.io.FileReader;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -46,6 +47,14 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
|||
*/
|
||||
public static HashMap<String, PlayerCache> playerCacheMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* HashSet of player names that have Mojang accounts.
|
||||
* If player is saved in here, they will be treated as online-mode ones.
|
||||
*/
|
||||
public static HashSet<String> mojangAccountNamesCache = new HashSet<>();
|
||||
|
||||
public static HashSet<String> accountStatusCache = new HashSet<>();
|
||||
|
||||
// Getting game directory
|
||||
public static final Path gameDirectory = FabricLoader.getInstance().getGameDir();
|
||||
|
||||
|
@ -62,6 +71,12 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
|||
// The support on discord was great! I really appreciate your help.
|
||||
logInfo("This mod wouldn't exist without the awesome Fabric Community. TYSM guys!");
|
||||
|
||||
try {
|
||||
serverProp.load(new FileReader(gameDirectory + "/server.properties"));
|
||||
} catch (IOException e) {
|
||||
logError("Error while reading server properties: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Creating data directory (database and config files are stored there)
|
||||
File file = new File(gameDirectory + "/mods/SimpleAuth/leveldbStore");
|
||||
if (!file.exists() && !file.mkdirs())
|
||||
|
@ -71,12 +86,6 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
|||
// Connecting to db
|
||||
DB.openConnection();
|
||||
|
||||
try {
|
||||
serverProp.load(new FileReader(gameDirectory + "/server.properties"));
|
||||
} catch (IOException e) {
|
||||
logError("Error while reading server properties: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
// Registering the commands
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
||||
|
@ -17,7 +18,9 @@ import org.samo_lego.simpleauth.SimpleAuth;
|
|||
import org.samo_lego.simpleauth.event.item.DropItemCallback;
|
||||
import org.samo_lego.simpleauth.storage.PlayerCache;
|
||||
import org.samo_lego.simpleauth.utils.PlayerAuth;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
@ -26,10 +29,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||
import static org.samo_lego.simpleauth.SimpleAuth.config;
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap;
|
||||
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class MixinPlayerEntity implements PlayerAuth {
|
||||
|
||||
@Shadow @Final private GameProfile gameProfile;
|
||||
private final ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
|
||||
|
||||
// * 20 for 20 ticks in second
|
||||
|
@ -50,6 +55,8 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
|
|||
assert server != null;
|
||||
|
||||
PlayerCache cache = playerCacheMap.get(this.getFakeUuid());
|
||||
if(config.main.spawnOnJoin)
|
||||
logInfo("Teleporting " + player.getName().asString() + (hide ? " to spawn." : " to original position."));
|
||||
if (hide) {
|
||||
// Saving position
|
||||
cache.lastLocation.dimension = player.getServerWorld();
|
||||
|
@ -90,14 +97,15 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
|
|||
public String getFakeUuid() {
|
||||
// If server is in online mode online-mode UUIDs should be used
|
||||
assert server != null;
|
||||
if(server.isOnlineMode())
|
||||
if(server.isOnlineMode() && this.isUsingMojangAccount())
|
||||
return player.getUuidAsString();
|
||||
/*
|
||||
Lower case is used for Player and PlAyEr to get same UUID (for password storing)
|
||||
Mimicking Mojang behaviour, where players cannot set their name to
|
||||
ExAmple if Example is already taken.
|
||||
*/
|
||||
String playername = player.getName().asString().toLowerCase();
|
||||
// Getting player+s name via GameProfile, in order to be compatible with Drogtor mod
|
||||
String playername = player.getGameProfile().getName().toLowerCase();
|
||||
return PlayerEntity.getOfflinePlayerUuid(playername).toString();
|
||||
|
||||
}
|
||||
|
@ -172,14 +180,23 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks whether player is a fake player (from CarpetMod).
|
||||
* Checks whether player can skip authentication process.
|
||||
*
|
||||
* @return true if player is fake (can skip authentication process), otherwise false
|
||||
* @return true if can skip authentication process, otherwise false
|
||||
*/
|
||||
@Override
|
||||
public boolean canSkipAuth() {
|
||||
// We ask CarpetHelper class since it has the imports needed
|
||||
return this.isRunningCarpet && isPlayerCarpetFake(this.player);
|
||||
return (this.isRunningCarpet && isPlayerCarpetFake(this.player)) || (isUsingMojangAccount() && config.experimental.premiumAutologin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the player is using the mojang account.
|
||||
* @return true if paid, otherwise false
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsingMojangAccount() {
|
||||
return !gameProfile.getId().equals(PlayerEntity.getOfflinePlayerUuid(gameProfile.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,7 +207,7 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
|
|||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
String uuid = ((PlayerAuth) player).getFakeUuid();
|
||||
return playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated;
|
||||
return this.canSkipAuth() || (playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated);
|
||||
}
|
||||
|
||||
@Inject(method = "tick()V", at = @At("HEAD"), cancellable = true)
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.packet.c2s.login.LoginHelloC2SPacket;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.*;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logError;
|
||||
|
||||
@Mixin(ServerLoginNetworkHandler.class)
|
||||
public class MixinServerLoginNetworkHandler {
|
||||
|
||||
@Shadow
|
||||
private GameProfile profile;
|
||||
@Shadow
|
||||
private int loginTicks;
|
||||
|
||||
/**
|
||||
* Fake state of current player.
|
||||
*/
|
||||
private boolean acceptCrackedPlayer = false;
|
||||
|
||||
/**
|
||||
* Mimicks the ticking if autologin is enabled.
|
||||
* @param ci
|
||||
*/
|
||||
@Inject(method = "tick()V", at = @At("HEAD"), cancellable = true)
|
||||
private void preTick(CallbackInfo ci) {
|
||||
if (this.acceptCrackedPlayer && config.experimental.premiumAutologin) {
|
||||
((ServerLoginNetworkHandler) (Object) this).acceptPlayer();
|
||||
|
||||
if (this.loginTicks++ == 600)
|
||||
((ServerLoginNetworkHandler) (Object) this).disconnect(new TranslatableText("multiplayer.disconnect.slow_login"));
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the player has purchased an account.
|
||||
* If so, server is presented as online, and continues as in normal-online mode.
|
||||
* Otherwise, player is marked as ready to be accepted into the game.
|
||||
* @param packet
|
||||
* @param ci
|
||||
*/
|
||||
@Inject(
|
||||
method = "onHello(Lnet/minecraft/network/packet/c2s/login/LoginHelloC2SPacket;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/network/packet/c2s/login/LoginHelloC2SPacket;getProfile()Lcom/mojang/authlib/GameProfile;",
|
||||
shift = At.Shift.AFTER
|
||||
),
|
||||
cancellable = true
|
||||
)
|
||||
private void checkPremium(LoginHelloC2SPacket packet, CallbackInfo ci) {
|
||||
if(config.experimental.premiumAutologin) {
|
||||
try {
|
||||
String playername = packet.getProfile().getName().toLowerCase();
|
||||
if(playerCacheMap.containsKey(PlayerEntity.getOfflinePlayerUuid(playername).toString())) {
|
||||
// Player definitely doesn't have a mojang account
|
||||
this.acceptCrackedPlayer = true;
|
||||
|
||||
this.profile = packet.getProfile();
|
||||
ci.cancel();
|
||||
}
|
||||
else if(!mojangAccountNamesCache.contains(playername)) {
|
||||
// Checking account status from API
|
||||
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL("https://api.mojang.com/users/profiles/minecraft/" + playername).openConnection();
|
||||
httpsURLConnection.setRequestMethod("GET");
|
||||
httpsURLConnection.setConnectTimeout(5000);
|
||||
httpsURLConnection.setReadTimeout(5000);
|
||||
|
||||
int response = httpsURLConnection.getResponseCode();
|
||||
if (response == HttpURLConnection.HTTP_OK) {
|
||||
// Player has a Mojang account
|
||||
httpsURLConnection.disconnect();
|
||||
|
||||
|
||||
// Caches the request
|
||||
mojangAccountNamesCache.add(playername);
|
||||
// Authentication continues in original method
|
||||
}
|
||||
else if(response == HttpURLConnection.HTTP_NO_CONTENT) {
|
||||
// Player doesn't have a Mojang account
|
||||
httpsURLConnection.disconnect();
|
||||
this.acceptCrackedPlayer = true;
|
||||
|
||||
this.profile = packet.getProfile();
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import com.google.gson.GsonBuilder;
|
|||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.serverProp;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logError;
|
||||
|
||||
public class AuthConfig {
|
||||
|
@ -191,6 +192,12 @@ public class AuthConfig {
|
|||
* @see <a href="https://github.com/samolego/SimpleAuth/wiki/GLIBC-problems" target="_blank">wiki</a>
|
||||
*/
|
||||
public boolean useBCryptLibrary = false;
|
||||
/**
|
||||
* Whether players who have a valid session should skip the authentication process.
|
||||
* You have to set online-mode to true in server.properties!
|
||||
* (cracked players will still be able to enter, but they'll need to login)
|
||||
*/
|
||||
public boolean premiumAutologin = false;
|
||||
}
|
||||
|
||||
public MainConfig main = new MainConfig();
|
||||
|
@ -212,6 +219,10 @@ public class AuthConfig {
|
|||
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)
|
||||
)) {
|
||||
config = gson.fromJson(fileReader, AuthConfig.class);
|
||||
if(config.experimental.premiumAutologin && !Boolean.parseBoolean(serverProp.getProperty("online-mode"))) {
|
||||
logError("You cannot use server in offline mode and premiumAutologin! Disabling the latter.");
|
||||
config.experimental.premiumAutologin = false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("[SimpleAuth] Problem occurred when trying to load config: ", e);
|
||||
}
|
||||
|
|
|
@ -58,4 +58,10 @@ public interface PlayerAuth {
|
|||
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
|
||||
*/
|
||||
boolean canSkipAuth();
|
||||
|
||||
/**
|
||||
* Whether the player is using the mojang account
|
||||
* @return true if paid, false if cracked
|
||||
*/
|
||||
boolean isUsingMojangAccount();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"server": [
|
||||
"MixinPlayerEntity",
|
||||
"MixinPlayerManager",
|
||||
"MixinServerLoginNetworkHandler",
|
||||
"MixinServerPlayNetworkHandler",
|
||||
"MixinSlot"
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue