diff --git a/.gitignore b/.gitignore index d5f737e..dd69690 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,5 @@ runs/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar + +.kotlin \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index eff3370..8de81c8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,8 @@ plugins { loom { serverOnlyMinecraftJar() + + accessWidenerPath = file("src/main/resources/minekord.accesswidener") } val modVersion: String by project diff --git a/src/main/java/ua/mei/minekord/mixin/ServerLoginNetworkHandlerMixin.java b/src/main/java/ua/mei/minekord/mixin/ServerLoginNetworkHandlerMixin.java index 7349ca8..f9712b6 100644 --- a/src/main/java/ua/mei/minekord/mixin/ServerLoginNetworkHandlerMixin.java +++ b/src/main/java/ua/mei/minekord/mixin/ServerLoginNetworkHandlerMixin.java @@ -18,19 +18,25 @@ @Mixin(ServerLoginNetworkHandler.class) public abstract class ServerLoginNetworkHandlerMixin { - @Shadow public abstract void disconnect(Text text); + @Shadow + @Final + MinecraftServer server; - @Shadow @Nullable private GameProfile profile; + @Shadow + @Nullable + private GameProfile profile; - @Shadow protected abstract void sendSuccessPacket(GameProfile gameProfile); + @Shadow + public abstract void disconnect(Text text); - @Shadow @Final MinecraftServer server; + @Shadow + protected abstract void sendSuccessPacket(GameProfile gameProfile); @Inject(method = "onHello", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;isOnlineMode()Z"), cancellable = true) private void minekord$trueUuids(LoginHelloC2SPacket loginHelloC2SPacket, CallbackInfo ci) { if (SnowflakeToUUID.INSTANCE.enabled()) { if (this.server.isOnlineMode() && !SnowflakeToUUID.INSTANCE.allowOfflinePlayers() && !SnowflakeToUUID.INSTANCE.premiumPlayer(loginHelloC2SPacket.comp_907())) { - this.disconnect(Text.literal("test1")); + this.disconnect(Text.translatable("multiplayer.disconnect.generic")); ci.cancel(); } @@ -40,10 +46,19 @@ public abstract class ServerLoginNetworkHandlerMixin { this.profile = new GameProfile(trueUuid, loginHelloC2SPacket.comp_765()); this.sendSuccessPacket(this.profile); } else { - this.disconnect(Text.literal("test2")); + this.disconnect(Text.translatable("multiplayer.disconnect.generic")); } ci.cancel(); } } + + @Inject(method = "sendSuccessPacket", at = @At("HEAD")) + private void minekord$checkRoles(GameProfile gameProfile, CallbackInfo ci) { + if (!SnowflakeToUUID.INSTANCE.enabled()) { + if (SnowflakeToUUID.INSTANCE.getPlayer(gameProfile.getName()) == null) { + this.disconnect(Text.translatable("multiplayer.disconnect.generic")); + } + } + } } diff --git a/src/main/kotlin/ua/mei/minekord/auth/SnowflakeToUUID.kt b/src/main/kotlin/ua/mei/minekord/auth/SnowflakeToUUID.kt index 92ec9d0..975bd71 100644 --- a/src/main/kotlin/ua/mei/minekord/auth/SnowflakeToUUID.kt +++ b/src/main/kotlin/ua/mei/minekord/auth/SnowflakeToUUID.kt @@ -4,9 +4,7 @@ import dev.kord.common.annotation.KordExperimental import dev.kord.core.entity.Member import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.plugins.timeout import io.ktor.client.request.get -import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpStatusCode import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.firstOrNull @@ -35,12 +33,19 @@ object SnowflakeToUUID { } @OptIn(KordExperimental::class) + fun getPlayer(nickname: String): Member? { + return runBlocking { + MinekordBot.guild?.getMembers(nickname)?.filter { member -> + val trueRoleIds: List = member.roleIds.map { it.value } + config[AuthSpec.requiredRoles].all { it in trueRoleIds } + }?.firstOrNull() + } + } + fun generateFromNickname(nickname: String): UUID? { return runBlocking { try { - val member: Member = MinekordBot.guild?.getMembers(nickname)?.filter { - it.roleIds.map { it.value }.all { it in config[AuthSpec.requiredRoles] } - }?.firstOrNull() ?: return@runBlocking null + val member: Member = getPlayer(nickname) ?: return@runBlocking null generateFromId(member.id.value) } catch (_: Throwable) { diff --git a/src/main/kotlin/ua/mei/minekord/config/AuthSpec.kt b/src/main/kotlin/ua/mei/minekord/config/AuthSpec.kt index 6967df6..a608ee0 100644 --- a/src/main/kotlin/ua/mei/minekord/config/AuthSpec.kt +++ b/src/main/kotlin/ua/mei/minekord/config/AuthSpec.kt @@ -6,4 +6,6 @@ object AuthSpec : ConfigSpec() { val uuidFromSnowflake by required() val allowOfflinePlayers by required() val requiredRoles by required>() + val loginByIp by required() + val ignorePremiumIp by required() } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index ee08955..0178b7d 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -19,6 +19,7 @@ "mixins": [ "minekord.mixins.json" ], + "accessWidener": "minekord.accesswidener", "depends": { "fabricloader": ">=${loader_version}", "fabric-language-kotlin": ">=${kotlin_loader_version}", diff --git a/src/main/resources/minekord.accesswidener b/src/main/resources/minekord.accesswidener new file mode 100644 index 0000000..cfd618b --- /dev/null +++ b/src/main/resources/minekord.accesswidener @@ -0,0 +1,3 @@ +accessWidener v2 named + +accessible class net/minecraft/server/network/ServerLoginNetworkHandler$State \ No newline at end of file diff --git a/src/main/resources/minekord.toml b/src/main/resources/minekord.toml index 3af9822..b4edddc 100644 --- a/src/main/resources/minekord.toml +++ b/src/main/resources/minekord.toml @@ -12,8 +12,24 @@ chat = 0 uuidFromSnowflake = false # If true, offline players can join server if online mode enabled. +# If online mode disabled, ignore. +# Made for servers which uses EasyAuth with premium-auto-login. allowOfflinePlayers = false # Roles which required for server join. # Can be empty. requiredRoles = [] + +# When player trying to join server, bot will send embed to player DM, +# embed contains IP from which player are joining, player can confirm +# is this ip are they, and then successful join server. +# +# Every new IP, bot will request confirmation. +# Player can join only with last confirmed IP. +# +# Option made for players, who are lazy to type /login and /register, +# or for best safety. +loginByIp = false + +# Don't request IP confirmation to premium players. +ignorePremiumIp = false