Merge branch 'fabric' into mongodb
# Conflicts: # src/main/java/org/samo_lego/simpleauth/SimpleAuth.java # src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.java # src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java # src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java # src/main/java/org/samo_lego/simpleauth/storage/database/LevelDB.java
This commit is contained in:
commit
2f7d4d5568
|
@ -21,7 +21,7 @@ echo "MC_VERSION=$mcVersion" >> $GITHUB_ENV
|
|||
|
||||
|
||||
# Checks if build is stable (I always bump version when I release stable, uploadable version)
|
||||
latestRelease=$(curl -s "https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest" | grep -oP '"tag_name": "\K(.*)(?=")')
|
||||
latestRelease=$(curl -s "https://api.github.com/repos/$GITHUB_REPOSITORY/releases" | grep -oP '(?<="tag_name": ")[^"]*' | head -n 1)
|
||||
echo "Latest release is: $latestRelease"
|
||||
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Fabric properties
|
||||
minecraft_version=1.16.3
|
||||
yarn_mappings=1.16.3+build.1
|
||||
loader_version=0.9.3+build.207
|
||||
minecraft_version=1.16.4
|
||||
yarn_mappings=1.16.4+build.1
|
||||
loader_version=0.10.6+build.214
|
||||
|
||||
#Fabric api
|
||||
fabric_version=0.20.2+build.402-1.16
|
||||
fabric_version=0.25.1+build.416-1.16
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.6.0
|
||||
mod_version = 1.6.5
|
||||
maven_group = org.samo_lego
|
||||
archives_base_name = simpleauth
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
#Thu Jun 04 11:39:28 CEST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -41,15 +41,23 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
|||
* It's cleared on server stop in order to save some interactions with database during runtime.
|
||||
* Stores their data as {@link org.samo_lego.simpleauth.storage.PlayerCache PlayerCache} object.
|
||||
*/
|
||||
public static HashMap<String, PlayerCache> playerCacheMap = new HashMap<>();
|
||||
public static final 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 final HashSet<String> mojangAccountNamesCache = new HashSet<>();
|
||||
|
||||
// Getting game directory
|
||||
public static final Path gameDirectory = FabricLoader.getInstance().getGameDir();
|
||||
|
||||
// Server properties
|
||||
public static Properties serverProp = new Properties();
|
||||
public static final Properties serverProp = new Properties();
|
||||
|
||||
// Mod config
|
||||
/**
|
||||
* Config of the SimpleAuth mod.
|
||||
*/
|
||||
public static AuthConfig config;
|
||||
|
||||
@Override
|
||||
|
@ -59,6 +67,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())
|
||||
|
@ -68,12 +82,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) -> {
|
||||
|
@ -113,7 +121,7 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
|||
// Closing threads
|
||||
try {
|
||||
THREADPOOL.shutdownNow();
|
||||
if (!THREADPOOL.awaitTermination(100, TimeUnit.MICROSECONDS)) {
|
||||
if (!THREADPOOL.awaitTermination(500, TimeUnit.MILLISECONDS)) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.samo_lego.simpleauth.utils.PlayerAuth;
|
|||
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.config;
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.mojangAccountNamesCache;
|
||||
|
||||
public class LogoutCommand {
|
||||
|
||||
|
@ -22,8 +23,12 @@ public class LogoutCommand {
|
|||
private static int logout(ServerCommandSource serverCommandSource) throws CommandSyntaxException {
|
||||
ServerPlayerEntity player = serverCommandSource.getPlayer();
|
||||
|
||||
if(!mojangAccountNamesCache.contains(player.getGameProfile().getName().toLowerCase())) {
|
||||
((PlayerAuth) player).setAuthenticated(false);
|
||||
player.sendMessage(new LiteralText(config.lang.successfulLogout), false);
|
||||
}
|
||||
else
|
||||
player.sendMessage(new LiteralText(config.lang.cannotLogout), false);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ public class AuthEventHandler {
|
|||
|
||||
// Player joining the server
|
||||
public static void onPlayerJoin(ServerPlayerEntity player) {
|
||||
// If player is fake auth is not needed
|
||||
if (((PlayerAuth) player).canSkipAuth())
|
||||
return;
|
||||
// Checking if session is still valid
|
||||
|
@ -76,22 +75,12 @@ public class AuthEventHandler {
|
|||
playerCache.validUntil >= System.currentTimeMillis() &&
|
||||
player.getIp().equals(playerCache.lastIp)
|
||||
) {
|
||||
// Valid session
|
||||
((PlayerAuth) player).setAuthenticated(true);
|
||||
return;
|
||||
}
|
||||
player.setInvulnerable(config.experimental.playerInvulnerable);
|
||||
player.setInvisible(config.experimental.playerInvisible);
|
||||
|
||||
// Invalidating session
|
||||
playerCache.isAuthenticated = false;
|
||||
if(config.main.spawnOnJoin)
|
||||
((PlayerAuth) player).hidePosition(true);
|
||||
}
|
||||
else {
|
||||
((PlayerAuth) player).setAuthenticated(false);
|
||||
playerCache = playerCacheMap.get(uuid);
|
||||
playerCache.wasOnFire = false;
|
||||
}
|
||||
|
||||
|
||||
// Tries to rescue player from nether portal
|
||||
|
@ -116,10 +105,8 @@ public class AuthEventHandler {
|
|||
String uuid = ((PlayerAuth) player).getFakeUuid();
|
||||
PlayerCache playerCache = playerCacheMap.get(uuid);
|
||||
|
||||
if(((PlayerAuth) player).isAuthenticated()) {
|
||||
if(playerCache.isAuthenticated) {
|
||||
playerCache.lastIp = player.getIp();
|
||||
playerCache.lastAir = player.getAir();
|
||||
playerCache.wasOnFire = player.isOnFire();
|
||||
playerCache.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL);
|
||||
|
||||
// Setting the session expire time
|
||||
|
@ -151,13 +138,8 @@ public class AuthEventHandler {
|
|||
public static ActionResult onPlayerMove(PlayerEntity player) {
|
||||
// Player will fall if enabled (prevent fly kick)
|
||||
boolean auth = ((PlayerAuth) player).isAuthenticated();
|
||||
if(!auth && config.main.allowFalling && !player.isOnGround() && !player.isInsideWaterOrBubbleColumn()) {
|
||||
if(player.isInvulnerable())
|
||||
player.setInvulnerable(false);
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
// Otherwise movement should be disabled
|
||||
else if(!auth && !config.experimental.allowMovement) {
|
||||
if(!auth && !config.experimental.allowMovement) {
|
||||
if(!player.isInvulnerable())
|
||||
player.setInvulnerable(true);
|
||||
return ActionResult.FAIL;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import net.minecraft.advancement.PlayerAdvancementTracker;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.ServerAdvancementLoader;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import org.samo_lego.simpleauth.utils.PlayerAuth;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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 java.io.File;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.config;
|
||||
|
||||
@Mixin(PlayerAdvancementTracker.class)
|
||||
public class MixinPlayerAdvancementTracker {
|
||||
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
private File advancementFile;
|
||||
|
||||
@Shadow
|
||||
private ServerPlayerEntity owner;
|
||||
|
||||
@Inject(method = "load(Lnet/minecraft/server/ServerAdvancementLoader;)V", at = @At("HEAD"))
|
||||
private void startMigratingOfflineAdvancements(ServerAdvancementLoader advancementLoader, CallbackInfo ci) {
|
||||
if(config.experimental.premiumAutologin && !config.experimental.forceoOfflineUuids && ((PlayerAuth) this.owner).isUsingMojangAccount() && !this.advancementFile.isFile()) {
|
||||
// Migrate
|
||||
String playername = owner.getGameProfile().getName();
|
||||
this.advancementFile = new File(this.advancementFile.getParent(), PlayerEntity.getOfflinePlayerUuid(playername).toString() + ".json");
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "load(Lnet/minecraft/server/ServerAdvancementLoader;)V", at = @At("TAIL"))
|
||||
private void endMigratingOfflineAdvancements(ServerAdvancementLoader advancementLoader, CallbackInfo ci) {
|
||||
if(config.experimental.premiumAutologin && !config.experimental.forceoOfflineUuids && ((PlayerAuth) this.owner).isUsingMojangAccount()) {
|
||||
this.advancementFile = new File(this.advancementFile.getParent(), owner.getUuid() + ".json");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,220 +1,17 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
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.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.*;
|
||||
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class MixinPlayerEntity implements PlayerAuth {
|
||||
public class MixinPlayerEntity {
|
||||
|
||||
private final ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
|
||||
|
||||
// * 20 for 20 ticks in second
|
||||
private int kickTimer = config.main.kickTime * 20;
|
||||
|
||||
private final boolean isRunningCarpet = FabricLoader.getInstance().isModLoaded("carpet");
|
||||
|
||||
private final MinecraftServer server = player.getServer();
|
||||
|
||||
/**
|
||||
* Teleports player to spawn or last location that is recorded.
|
||||
* Last location means the location before de-authentication.
|
||||
*
|
||||
* @param hide whether to teleport player to spawn (provided in config) or last recorded position
|
||||
*/
|
||||
@Override
|
||||
public void hidePosition(boolean hide) {
|
||||
assert server != null;
|
||||
|
||||
PlayerCache cache = playerCacheMap.get(this.getFakeUuid());
|
||||
System.out.println("Teleporting Player. hide:" + hide);
|
||||
if (hide) {
|
||||
// Saving position
|
||||
cache.lastLocation.dimension = player.getServerWorld();
|
||||
cache.lastLocation.position = player.getPos();
|
||||
cache.lastLocation.yaw = player.yaw;
|
||||
cache.lastLocation.pitch = player.pitch;
|
||||
|
||||
// Teleports player to spawn
|
||||
player.teleport(
|
||||
server.getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier(config.worldSpawn.dimension))),
|
||||
config.worldSpawn.x,
|
||||
config.worldSpawn.y,
|
||||
config.worldSpawn.z,
|
||||
config.worldSpawn.yaw,
|
||||
config.worldSpawn.pitch
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Puts player to last cached position
|
||||
player.teleport(
|
||||
cache.lastLocation.dimension,
|
||||
cache.lastLocation.position.getX(),
|
||||
cache.lastLocation.position.getY(),
|
||||
cache.lastLocation.position.getZ(),
|
||||
cache.lastLocation.yaw,
|
||||
cache.lastLocation.pitch
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts player uuid, to ensure player with "nAmE" and "NamE" get same uuid.
|
||||
* Both players are not allowed to play, since mod mimics Mojang behaviour.
|
||||
* of not allowing accounts with same names but different capitalization.
|
||||
*
|
||||
* @return converted UUID as string
|
||||
*/
|
||||
@Override
|
||||
public String getFakeUuid() {
|
||||
// If server is in online mode online-mode UUIDs should be used
|
||||
assert server != null;
|
||||
if(server.isOnlineMode())
|
||||
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();
|
||||
return PlayerEntity.getOfflinePlayerUuid(playername).toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication status of the player.
|
||||
*
|
||||
* @param authenticated whether player should be authenticated
|
||||
*/
|
||||
@Override
|
||||
public void setAuthenticated(boolean authenticated) {
|
||||
PlayerCache playerCache;
|
||||
|
||||
if(!playerCacheMap.containsKey(this.getFakeUuid())) {
|
||||
// First join
|
||||
String jsonString = DB.getUserData(this.getFakeUuid());
|
||||
if(jsonString != null && !jsonString.isEmpty())
|
||||
playerCache = PlayerCache.fromJson(player, jsonString);
|
||||
else
|
||||
playerCache = new PlayerCache(player);
|
||||
|
||||
// Saving to hashmap of player caches
|
||||
playerCacheMap.put(this.getFakeUuid(), playerCache);
|
||||
}
|
||||
else {
|
||||
playerCache = playerCacheMap.get(this.getFakeUuid());
|
||||
if(this.isAuthenticated() == authenticated)
|
||||
return;
|
||||
playerCache.isAuthenticated = authenticated;
|
||||
}
|
||||
|
||||
player.setInvulnerable(!authenticated && config.experimental.playerInvulnerable);
|
||||
player.setInvisible(!authenticated && config.experimental.playerInvisible);
|
||||
|
||||
// Teleporting player (hiding / restoring position)
|
||||
if(config.main.spawnOnJoin)
|
||||
this.hidePosition(!authenticated);
|
||||
|
||||
if(authenticated) {
|
||||
kickTimer = config.main.kickTime * 20;
|
||||
// Updating blocks if needed (if portal rescue action happened)
|
||||
if(playerCache.wasInPortal) {
|
||||
World world = player.getEntityWorld();
|
||||
BlockPos pos = player.getBlockPos();
|
||||
|
||||
// Sending updates to portal blocks
|
||||
// This is technically not needed, but it cleans the "messed portal" on the client
|
||||
world.updateListeners(pos, world.getBlockState(pos), world.getBlockState(pos), 3);
|
||||
world.updateListeners(pos.up(), world.getBlockState(pos.up()), world.getBlockState(pos.up()), 3);
|
||||
}
|
||||
|
||||
// Setting last air to player
|
||||
if(player.isSubmergedInWater())
|
||||
player.setAir(playerCache.lastAir);
|
||||
|
||||
// In case player is in lava during authentication proccess
|
||||
if(!playerCache.wasOnFire)
|
||||
player.setFireTicks(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text which tells the player
|
||||
* to login or register, depending on account status.
|
||||
*
|
||||
* @return LiteralText with appropriate string (login or register)
|
||||
*/
|
||||
@Override
|
||||
public Text getAuthMessage() {
|
||||
final PlayerCache cache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid());
|
||||
if(SimpleAuth.config.main.enableGlobalPassword || cache.isRegistered)
|
||||
return new LiteralText(
|
||||
SimpleAuth.config.lang.notAuthenticated + "\n" + SimpleAuth.config.lang.loginRequired
|
||||
);
|
||||
return new LiteralText(
|
||||
SimpleAuth.config.lang.notAuthenticated+ "\n" + SimpleAuth.config.lang.registerRequired
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether player is a fake player (from CarpetMod).
|
||||
*
|
||||
* @return true if player is fake (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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether player is authenticated.
|
||||
*
|
||||
* @return false if player is not authenticated, otherwise true.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
String uuid = ((PlayerAuth) player).getFakeUuid();
|
||||
return playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated;
|
||||
}
|
||||
|
||||
@Inject(method = "tick()V", at = @At("HEAD"), cancellable = true)
|
||||
private void tick(CallbackInfo ci) {
|
||||
if(!this.isAuthenticated()) {
|
||||
// Checking player timer
|
||||
if(kickTimer <= 0 && player.networkHandler.getConnection().isOpen()) {
|
||||
player.networkHandler.disconnect(new LiteralText(config.lang.timeExpired));
|
||||
}
|
||||
else {
|
||||
// Sending authentication prompt every 10 seconds
|
||||
if(kickTimer % 200 == 0)
|
||||
player.sendMessage(this.getAuthMessage(), false);
|
||||
kickTimer--;
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Player item dropping
|
||||
@Inject(method = "dropSelectedItem(Z)Z", at = @At("HEAD"), cancellable = true)
|
||||
|
@ -223,14 +20,6 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
|
|||
ActionResult result = DropItemCallback.EVENT.invoker().onDropItem(player);
|
||||
|
||||
if (result == ActionResult.FAIL) {
|
||||
// Canceling the item drop, as well as giving the items back to player (and updating inv with packet)
|
||||
player.networkHandler.sendPacket(
|
||||
new ScreenHandlerSlotUpdateS2CPacket(
|
||||
-2,
|
||||
player.inventory.selectedSlot,
|
||||
player.inventory.getStack(player.inventory.selectedSlot))
|
||||
);
|
||||
player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(-1, -1, player.inventory.getCursorStack()));
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,39 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.stat.ServerStatHandler;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback;
|
||||
import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback;
|
||||
import org.samo_lego.simpleauth.event.entity.player.PrePlayerJoinCallback;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.config;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public abstract class MixinPlayerManager {
|
||||
|
||||
@Shadow @Final private MinecraftServer server;
|
||||
|
||||
@Inject(method = "onPlayerConnect(Lnet/minecraft/network/ClientConnection;Lnet/minecraft/server/network/ServerPlayerEntity;)V", at = @At("RETURN"))
|
||||
private void onPlayerConnect(ClientConnection clientConnection, ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) {
|
||||
PlayerJoinServerCallback.EVENT.invoker().onPlayerJoin(serverPlayerEntity);
|
||||
|
@ -30,7 +44,6 @@ public abstract class MixinPlayerManager {
|
|||
PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity);
|
||||
}
|
||||
|
||||
// Method for kicking player for
|
||||
@Inject(method = "checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;", at = @At("HEAD"), cancellable = true)
|
||||
private void checkCanJoin(SocketAddress socketAddress, GameProfile profile, CallbackInfoReturnable<Text> cir) {
|
||||
// Getting the player that is trying to join the server
|
||||
|
@ -43,4 +56,35 @@ public abstract class MixinPlayerManager {
|
|||
cir.setReturnValue(returnText);
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(
|
||||
method = "createStatHandler(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/stat/ServerStatHandler;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;getName()Lnet/minecraft/text/Text;"
|
||||
),
|
||||
ordinal = 1
|
||||
)
|
||||
private File migrateOfflineStats(File file, PlayerEntity player) {
|
||||
if(config.experimental.premiumAutologin && !config.experimental.forceoOfflineUuids && ((PlayerAuth) player).isUsingMojangAccount()) {
|
||||
String playername = player.getGameProfile().getName();
|
||||
file = new File(file.getParent(), PlayerEntity.getOfflinePlayerUuid(playername) + ".json");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "createStatHandler(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/stat/ServerStatHandler;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void migrateOfflineStats(PlayerEntity player, CallbackInfoReturnable<ServerStatHandler> cir, UUID uUID, ServerStatHandler serverStatHandler, File serverStatsDir, File playerStatFile) {
|
||||
File onlineFile = new File(serverStatsDir, uUID + ".json");
|
||||
if(config.experimental.premiumAutologin && !config.experimental.forceoOfflineUuids && ((PlayerAuth) player).isUsingMojangAccount() && !onlineFile.exists()) {
|
||||
((ServerStatHandlerAccessor) serverStatHandler).setFile(onlineFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
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.Unique;
|
||||
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 java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.*;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logError;
|
||||
|
||||
@Mixin(ServerLoginNetworkHandler.class)
|
||||
public abstract class MixinServerLoginNetworkHandler {
|
||||
|
||||
@Shadow
|
||||
private GameProfile profile;
|
||||
@Shadow
|
||||
private int loginTicks;
|
||||
|
||||
@Shadow protected abstract GameProfile toOfflineProfile(GameProfile profile);
|
||||
|
||||
/**
|
||||
* Fake state of current player.
|
||||
*/
|
||||
@Unique
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "acceptPlayer()V", at = @At("HEAD"))
|
||||
private void acceptPlayer(CallbackInfo ci) {
|
||||
if(config.experimental.forceoOfflineUuids) {
|
||||
this.profile = this.toOfflineProfile(this.profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
Pattern pattern = Pattern.compile("^[a-z0-9_]{3,16}$");
|
||||
Matcher matcher = pattern.matcher(playername);
|
||||
if(playerCacheMap.containsKey(PlayerEntity.getOfflinePlayerUuid(playername).toString()) || !matcher.matches()) {
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
import org.samo_lego.simpleauth.SimpleAuth;
|
||||
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;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.*;
|
||||
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public class MixinServerPlayerEntity implements PlayerAuth {
|
||||
|
||||
private final ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
|
||||
|
||||
// * 20 for 20 ticks in second
|
||||
private int kickTimer = config.main.kickTime * 20;
|
||||
|
||||
private final boolean isRunningCarpet = FabricLoader.getInstance().isModLoaded("carpet");
|
||||
|
||||
@Final
|
||||
@Shadow
|
||||
public MinecraftServer server;
|
||||
|
||||
/**
|
||||
* Teleports player to spawn or last location that is recorded.
|
||||
* Last location means the location before de-authentication.
|
||||
*
|
||||
* @param hide whether to teleport player to spawn (provided in config) or last recorded position
|
||||
*/
|
||||
@Override
|
||||
public void hidePosition(boolean hide) {
|
||||
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();
|
||||
cache.lastLocation.position = player.getPos();
|
||||
cache.lastLocation.yaw = player.yaw;
|
||||
cache.lastLocation.pitch = player.pitch;
|
||||
|
||||
// Teleports player to spawn
|
||||
player.teleport(
|
||||
server.getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier(config.worldSpawn.dimension))),
|
||||
config.worldSpawn.x,
|
||||
config.worldSpawn.y,
|
||||
config.worldSpawn.z,
|
||||
config.worldSpawn.yaw,
|
||||
config.worldSpawn.pitch
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Puts player to last cached position
|
||||
player.teleport(
|
||||
cache.lastLocation.dimension,
|
||||
cache.lastLocation.position.getX(),
|
||||
cache.lastLocation.position.getY(),
|
||||
cache.lastLocation.position.getZ(),
|
||||
cache.lastLocation.yaw,
|
||||
cache.lastLocation.pitch
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts player uuid, to ensure player with "nAmE" and "NamE" get same uuid.
|
||||
* Both players are not allowed to play, since mod mimics Mojang behaviour.
|
||||
* of not allowing accounts with same names but different capitalization.
|
||||
*
|
||||
* @return converted UUID as string
|
||||
*/
|
||||
@Override
|
||||
public String getFakeUuid() {
|
||||
// If server is in online mode online-mode UUIDs should be used
|
||||
assert server != null;
|
||||
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.
|
||||
*/
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication status of the player.
|
||||
*
|
||||
* @param authenticated whether player should be authenticated
|
||||
*/
|
||||
@Override
|
||||
public void setAuthenticated(boolean authenticated) {
|
||||
PlayerCache playerCache;
|
||||
|
||||
if(!playerCacheMap.containsKey(this.getFakeUuid())) {
|
||||
// First join
|
||||
playerCache = new PlayerCache(this.getFakeUuid(), player);
|
||||
playerCacheMap.put(this.getFakeUuid(), playerCache);
|
||||
}
|
||||
else {
|
||||
playerCache = playerCacheMap.get(this.getFakeUuid());
|
||||
if(this.isAuthenticated() == authenticated)
|
||||
return;
|
||||
playerCache.isAuthenticated = authenticated;
|
||||
}
|
||||
|
||||
player.setInvulnerable(!authenticated && config.experimental.playerInvulnerable);
|
||||
player.setInvisible(!authenticated && config.experimental.playerInvisible);
|
||||
|
||||
// Teleporting player (hiding / restoring position)
|
||||
if(config.main.spawnOnJoin)
|
||||
this.hidePosition(!authenticated);
|
||||
|
||||
if(authenticated) {
|
||||
kickTimer = config.main.kickTime * 20;
|
||||
// Updating blocks if needed (if portal rescue action happened)
|
||||
if(playerCache.wasInPortal) {
|
||||
World world = player.getEntityWorld();
|
||||
BlockPos pos = player.getBlockPos();
|
||||
|
||||
// Sending updates to portal blocks
|
||||
// This is technically not needed, but it cleans the "messed portal" on the client
|
||||
world.updateListeners(pos, world.getBlockState(pos), world.getBlockState(pos), 3);
|
||||
world.updateListeners(pos.up(), world.getBlockState(pos.up()), world.getBlockState(pos.up()), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text which tells the player
|
||||
* to login or register, depending on account status.
|
||||
*
|
||||
* @return LiteralText with appropriate string (login or register)
|
||||
*/
|
||||
@Override
|
||||
public Text getAuthMessage() {
|
||||
final PlayerCache cache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid());
|
||||
if(SimpleAuth.config.main.enableGlobalPassword || cache.isRegistered)
|
||||
return new LiteralText(
|
||||
SimpleAuth.config.lang.notAuthenticated + "\n" + SimpleAuth.config.lang.loginRequired
|
||||
);
|
||||
return new LiteralText(
|
||||
SimpleAuth.config.lang.notAuthenticated+ "\n" + SimpleAuth.config.lang.registerRequired
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether player can skip authentication process.
|
||||
*
|
||||
* @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)) || (isUsingMojangAccount() && config.experimental.premiumAutologin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the player is using the mojang account.
|
||||
* @return true if paid, otherwise false
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsingMojangAccount() {
|
||||
return mojangAccountNamesCache.contains(player.getGameProfile().getName().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether player is authenticated.
|
||||
*
|
||||
* @return false if player is not authenticated, otherwise true.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
String uuid = ((PlayerAuth) player).getFakeUuid();
|
||||
return this.canSkipAuth() || (playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated);
|
||||
}
|
||||
|
||||
@Inject(method = "playerTick()V", at = @At("HEAD"), cancellable = true)
|
||||
private void playerTick(CallbackInfo ci) {
|
||||
if(!this.isAuthenticated()) {
|
||||
// Checking player timer
|
||||
if(kickTimer <= 0 && player.networkHandler.getConnection().isOpen()) {
|
||||
player.networkHandler.disconnect(new LiteralText(config.lang.timeExpired));
|
||||
}
|
||||
else {
|
||||
// Sending authentication prompt every 10 seconds
|
||||
if(kickTimer % 200 == 0)
|
||||
player.sendMessage(this.getAuthMessage(), false);
|
||||
--kickTimer;
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.world.WorldSaveHandler;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.config;
|
||||
import static org.samo_lego.simpleauth.SimpleAuth.mojangAccountNamesCache;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
|
||||
|
||||
@Mixin(WorldSaveHandler.class)
|
||||
public class MixinWorldSaveHandler {
|
||||
|
||||
@Final
|
||||
@Shadow
|
||||
private File playerDataDir;
|
||||
|
||||
@Unique
|
||||
private boolean fileExists;
|
||||
|
||||
@Final
|
||||
@Shadow
|
||||
private static Logger LOGGER;
|
||||
|
||||
/**
|
||||
* Saves whether player save file exists.
|
||||
*
|
||||
* @param playerEntity
|
||||
* @param cir
|
||||
* @param compoundTag
|
||||
* @param file
|
||||
*/
|
||||
@Inject(
|
||||
method = "loadPlayerData(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/nbt/CompoundTag;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/io/File;exists()Z"
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void fileExists(PlayerEntity playerEntity, CallbackInfoReturnable<CompoundTag> cir, CompoundTag compoundTag, File file) {
|
||||
// @ModifyVariable cannot capture locals
|
||||
this.fileExists = file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads offline-uuid player data to compoundTag in order to migrate from offline to online.
|
||||
*
|
||||
* @param compoundTag null compound tag.
|
||||
* @param player player who might need migration of datd.
|
||||
* @return compoundTag containing migrated data.
|
||||
*/
|
||||
@ModifyVariable(
|
||||
method = "loadPlayerData(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/nbt/CompoundTag;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/io/File;exists()Z"
|
||||
)
|
||||
)
|
||||
private CompoundTag migratePlayerData(CompoundTag compoundTag, PlayerEntity player) {
|
||||
// Checking for offline player data only if online doesn't exist yet
|
||||
String playername = player.getGameProfile().getName().toLowerCase();
|
||||
if(config.experimental.premiumAutologin && mojangAccountNamesCache.contains(playername) && !this.fileExists) {
|
||||
if(config.experimental.debugMode)
|
||||
logInfo("Migrating data for " + playername);
|
||||
File file = new File(this.playerDataDir, PlayerEntity.getOfflinePlayerUuid(player.getGameProfile().getName()) + ".dat");
|
||||
if (file.exists() && file.isFile())
|
||||
try {
|
||||
compoundTag = NbtIo.readCompressed(new FileInputStream(file));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.warn("Failed to load player data for {}", playername);
|
||||
}
|
||||
}
|
||||
else if(config.experimental.debugMode)
|
||||
logInfo("Not migrating " +
|
||||
playername +
|
||||
", as premium status is: " +
|
||||
mojangAccountNamesCache.contains(playername) +
|
||||
" and data file is " + (this.fileExists ? "" : "not") +
|
||||
" present."
|
||||
);
|
||||
return compoundTag;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.samo_lego.simpleauth.mixin;
|
||||
|
||||
import net.minecraft.stat.ServerStatHandler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Mixin(ServerStatHandler.class)
|
||||
public interface ServerStatHandlerAccessor {
|
||||
|
||||
@Accessor("file")
|
||||
File getFile();
|
||||
|
||||
@Accessor("file")
|
||||
void setFile(File file);
|
||||
}
|
|
@ -23,7 +23,9 @@ 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;
|
||||
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
|
||||
|
||||
public class AuthConfig {
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
|
@ -80,11 +82,6 @@ public class AuthConfig {
|
|||
*/
|
||||
public int sessionTimeoutTime = 60;
|
||||
|
||||
/**
|
||||
* Should deauthenticated players fall if the login mid-air?
|
||||
*/
|
||||
public boolean allowFalling = false;
|
||||
|
||||
/**
|
||||
* Whether to tp player to spawn when joining (to hide original player coordinates).
|
||||
*/
|
||||
|
@ -145,7 +142,7 @@ public class AuthConfig {
|
|||
public String timeExpired = "§cTime for authentication has expired.";
|
||||
public String registerRequired = "§6Type /register <password> <password> to claim this account.";
|
||||
public String alreadyRegistered = "§6This account name is already registered!";
|
||||
public String registerSuccess = "§aYou are now authenticated.";
|
||||
public String registerSuccess = "§aAccount was created.";
|
||||
public String userdataDeleted = "§aUserdata deleted.";
|
||||
public String userdataUpdated = "§aUserdata updated.";
|
||||
public String accountDeleted = "§aYour account was successfully deleted!";
|
||||
|
@ -157,6 +154,7 @@ public class AuthConfig {
|
|||
public String worldSpawnSet = "§aSpawn for logging in was set successfully.";
|
||||
public String corruptedPlayerData = "§cYour data is probably corrupted. Please contact admin.";
|
||||
public String userNotRegistered = "§cThis player is not registered!";
|
||||
public String cannotLogout = "§cYou cannot logout!";
|
||||
}
|
||||
public static class ExperimentalConfig {
|
||||
/**
|
||||
|
@ -212,6 +210,27 @@ 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)
|
||||
*
|
||||
* This protects premium usernames from being stolen, since cracked players
|
||||
* with name that is found in Mojang database, are kicked.
|
||||
*/
|
||||
public boolean premiumAutologin = false;
|
||||
/**
|
||||
* Whether to modify player uuids to offline style.
|
||||
* Note: this should be used only if you had your server
|
||||
* running in offline mode and you made the switch to use
|
||||
* AuthConfig#premiumAutoLogin AND your players already
|
||||
* have e.g. villager discounts, which are based on uuid.
|
||||
* Other things (advancements, playerdata) are migrated
|
||||
* automatically, so think before enabling this. In case
|
||||
* an online-mode player changes username, they'll loose all
|
||||
* their stuff, unless you migrate it manually.
|
||||
*/
|
||||
public boolean forceoOfflineUuids = false;
|
||||
}
|
||||
|
||||
public MainConfig main = new MainConfig();
|
||||
|
@ -234,6 +253,16 @@ public class AuthConfig {
|
|||
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)
|
||||
)) {
|
||||
config = gson.fromJson(fileReader, AuthConfig.class);
|
||||
if(!Boolean.parseBoolean(serverProp.getProperty("online-mode"))) {
|
||||
if(config.experimental.forceoOfflineUuids) {
|
||||
logInfo("Server is in offline mode, forceoOfflineUuids option is irrelevant. Setting it to false.");
|
||||
config.experimental.forceoOfflineUuids = false;
|
||||
}
|
||||
if(config.experimental.premiumAutologin) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package org.samo_lego.simpleauth.storage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.*;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -46,8 +49,6 @@ public class PlayerCache {
|
|||
/**
|
||||
* Player stats before de-authentication.
|
||||
*/
|
||||
public int lastAir;
|
||||
public boolean wasOnFire;
|
||||
public boolean wasInPortal;
|
||||
|
||||
/**
|
||||
|
@ -60,7 +61,7 @@ public class PlayerCache {
|
|||
public float pitch;
|
||||
}
|
||||
|
||||
public PlayerCache.LastLocation lastLocation = new PlayerCache.LastLocation();
|
||||
public final PlayerCache.LastLocation lastLocation = new PlayerCache.LastLocation();
|
||||
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
|
@ -79,10 +80,6 @@ public class PlayerCache {
|
|||
if(player != null) {
|
||||
this.lastIp = player.getIp();
|
||||
|
||||
this.wasOnFire = player.isOnFire();
|
||||
this.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL);
|
||||
this.lastAir = player.getAir();
|
||||
|
||||
// Setting position cache
|
||||
this.lastLocation.dimension = player.getServerWorld();
|
||||
this.lastLocation.position = player.getPos();
|
||||
|
@ -92,9 +89,7 @@ public class PlayerCache {
|
|||
this.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL);
|
||||
}
|
||||
else {
|
||||
this.wasOnFire = false;
|
||||
this.wasInPortal = false;
|
||||
this.lastAir = 300;
|
||||
}
|
||||
|
||||
this.isRegistered = false;
|
||||
|
|
|
@ -63,7 +63,8 @@ public class LevelDB {
|
|||
* @param data data to put inside database
|
||||
* @return true if operation was successful, otherwise false
|
||||
*/
|
||||
public static boolean registerUser(String uuid, String data) {
|
||||
@Deprecated
|
||||
public boolean registerUser(String uuid, String data) {
|
||||
try {
|
||||
if(!isUserRegistered(uuid)) {
|
||||
levelDBStore.put(bytes("UUID:" + uuid), bytes("data:" + data));
|
||||
|
@ -110,7 +111,8 @@ public class LevelDB {
|
|||
* @param uuid uuid of the player to update data for
|
||||
* @param data data to put inside database
|
||||
*/
|
||||
public static void updateUserData(String uuid, String data) {
|
||||
@Deprecated
|
||||
public void updateUserData(String uuid, String data) {
|
||||
try {
|
||||
levelDBStore.put(bytes("UUID:" + uuid), bytes("data:" + data));
|
||||
} catch (Error e) {
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package org.samo_lego.simpleauth.utils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.samo_lego.simpleauth.SimpleAuth;
|
||||
import org.samo_lego.simpleauth.utils.hashing.HasherArgon2;
|
||||
import org.samo_lego.simpleauth.utils.hashing.HasherBCrypt;
|
||||
|
||||
|
@ -12,9 +7,6 @@ import static org.samo_lego.simpleauth.SimpleAuth.config;
|
|||
import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap;
|
||||
|
||||
public class AuthHelper {
|
||||
// Json parser
|
||||
private static final JsonParser parser = new JsonParser();
|
||||
|
||||
/**
|
||||
* Checks password of user
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -4,10 +4,15 @@
|
|||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [],
|
||||
"server": [
|
||||
"MixinPlayerAdvancementTracker",
|
||||
"MixinPlayerEntity",
|
||||
"MixinPlayerManager",
|
||||
"MixinServerLoginNetworkHandler",
|
||||
"MixinServerPlayerEntity",
|
||||
"MixinServerPlayNetworkHandler",
|
||||
"MixinSlot"
|
||||
"MixinSlot",
|
||||
"MixinWorldSaveHandler",
|
||||
"ServerStatHandlerAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in New Issue