Skip to content

Commit 5c0f911

Browse files
committed
caching ips & other things
1 parent 0cae670 commit 5c0f911

File tree

10 files changed

+193
-28
lines changed

10 files changed

+193
-28
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package ua.mei.minekord.mixin;
2+
3+
import com.mojang.authlib.GameProfile;
4+
import net.minecraft.server.PlayerManager;
5+
import net.minecraft.text.Text;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import org.spongepowered.asm.mixin.injection.Inject;
9+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
10+
import ua.mei.minekord.bot.DiscordUtils;
11+
import ua.mei.minekord.config.AuthSpec;
12+
import ua.mei.minekord.config.MinekordConfigKt;
13+
import ua.mei.minekord.event.IPCheckEvent;
14+
15+
import java.net.SocketAddress;
16+
17+
@Mixin(PlayerManager.class)
18+
public class PlayerManagerMixin {
19+
@Inject(method = "checkCanJoin", at = @At("RETURN"), cancellable = true)
20+
private void minekord$checkRoles(SocketAddress socketAddress, GameProfile gameProfile, CallbackInfoReturnable<Text> cir) {
21+
if (cir.getReturnValue() == null) {
22+
if (!MinekordConfigKt.getConfig().get(AuthSpec.INSTANCE.getUuidFromSnowflake()) && !MinekordConfigKt.getConfig().get(AuthSpec.INSTANCE.getRequiredRoles()).isEmpty()) {
23+
if (DiscordUtils.INSTANCE.getPlayer(gameProfile.getName()) == null) {
24+
cir.setReturnValue(Text.translatable("multiplayer.disconnect.generic"));
25+
}
26+
}
27+
IPCheckEvent.Companion.getEvent().invoker().request(socketAddress, gameProfile);
28+
}
29+
}
30+
}

src/main/java/ua/mei/minekord/mixin/ServerLoginNetworkHandlerMixin.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
import org.spongepowered.asm.mixin.injection.At;
1313
import org.spongepowered.asm.mixin.injection.Inject;
1414
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
15-
import ua.mei.minekord.auth.SnowflakeToUUID;
15+
import ua.mei.minekord.bot.DiscordUtils;
16+
import ua.mei.minekord.config.AuthSpec;
17+
import ua.mei.minekord.config.MinekordConfigKt;
1618

1719
import java.util.UUID;
1820

@@ -34,13 +36,13 @@ public abstract class ServerLoginNetworkHandlerMixin {
3436

3537
@Inject(method = "onHello", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;isOnlineMode()Z"), cancellable = true)
3638
private void minekord$trueUuids(LoginHelloC2SPacket loginHelloC2SPacket, CallbackInfo ci) {
37-
if (SnowflakeToUUID.INSTANCE.enabled()) {
38-
if (this.server.isOnlineMode() && !SnowflakeToUUID.INSTANCE.allowOfflinePlayers() && !SnowflakeToUUID.INSTANCE.premiumPlayer(loginHelloC2SPacket.comp_907())) {
39+
if (MinekordConfigKt.getConfig().get(AuthSpec.INSTANCE.getUuidFromSnowflake())) {
40+
if (this.server.isOnlineMode() && !MinekordConfigKt.getConfig().get(AuthSpec.INSTANCE.getAllowOfflinePlayers()) && !DiscordUtils.INSTANCE.premiumPlayer(loginHelloC2SPacket.comp_907())) {
3941
this.disconnect(Text.translatable("multiplayer.disconnect.generic"));
4042
ci.cancel();
4143
}
4244

43-
UUID trueUuid = SnowflakeToUUID.INSTANCE.generateFromNickname(loginHelloC2SPacket.comp_765());
45+
UUID trueUuid = DiscordUtils.INSTANCE.generateFromNickname(loginHelloC2SPacket.comp_765());
4446

4547
if (trueUuid != null) {
4648
this.profile = new GameProfile(trueUuid, loginHelloC2SPacket.comp_765());
@@ -52,13 +54,4 @@ public abstract class ServerLoginNetworkHandlerMixin {
5254
ci.cancel();
5355
}
5456
}
55-
56-
@Inject(method = "sendSuccessPacket", at = @At("HEAD"))
57-
private void minekord$checkRoles(GameProfile gameProfile, CallbackInfo ci) {
58-
if (!SnowflakeToUUID.INSTANCE.enabled()) {
59-
if (SnowflakeToUUID.INSTANCE.getPlayer(gameProfile.getName()) == null) {
60-
this.disconnect(Text.translatable("multiplayer.disconnect.generic"));
61-
}
62-
}
63-
}
6457
}

src/main/kotlin/ua/mei/minekord/Minekord.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import net.fabricmc.loader.api.FabricLoader
66
import org.apache.logging.log4j.LogManager
77
import org.apache.logging.log4j.Logger
88
import ua.mei.minekord.bot.MinekordBot
9+
import ua.mei.minekord.cache.IPCache
10+
import ua.mei.minekord.config.AuthSpec
911
import ua.mei.minekord.config.CONFIG_PATH
1012
import ua.mei.minekord.config.config
1113
import java.nio.file.Files
@@ -27,6 +29,8 @@ object Minekord : ModInitializer {
2729

2830
config.validateRequired()
2931

30-
ServerLifecycleEvents.SERVER_STARTING.register { MinekordBot.launchBot() }
32+
if (config[AuthSpec.loginByIp]) IPCache.load()
33+
34+
MinekordBot.launchBot()
3135
}
3236
}

src/main/kotlin/ua/mei/minekord/auth/SnowflakeToUUID.kt renamed to src/main/kotlin/ua/mei/minekord/bot/DiscordUtils.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
1-
package ua.mei.minekord.auth
1+
package ua.mei.minekord.bot
22

33
import dev.kord.common.annotation.KordExperimental
44
import dev.kord.core.entity.Member
55
import io.ktor.client.HttpClient
6-
import io.ktor.client.engine.okhttp.OkHttp
76
import io.ktor.client.request.get
87
import io.ktor.http.HttpStatusCode
98
import kotlinx.coroutines.flow.filter
109
import kotlinx.coroutines.flow.firstOrNull
1110
import kotlinx.coroutines.runBlocking
12-
import ua.mei.minekord.bot.MinekordBot
1311
import ua.mei.minekord.config.AuthSpec
1412
import ua.mei.minekord.config.config
1513
import java.util.UUID
1614
import kotlin.experimental.inv
1715

18-
object SnowflakeToUUID {
19-
private val client: HttpClient = HttpClient(OkHttp)
20-
21-
fun enabled(): Boolean = config[AuthSpec.uuidFromSnowflake]
22-
fun allowOfflinePlayers(): Boolean = config[AuthSpec.allowOfflinePlayers]
16+
object DiscordUtils {
17+
private val client: HttpClient = HttpClient()
2318

2419
fun premiumPlayer(uuid: UUID): Boolean {
2520
return runBlocking {
@@ -47,14 +42,14 @@ object SnowflakeToUUID {
4742
try {
4843
val member: Member = getPlayer(nickname) ?: return@runBlocking null
4944

50-
generateFromId(member.id.value)
45+
discordToUuid(member.id.value)
5146
} catch (_: Throwable) {
5247
null
5348
}
5449
}
5550
}
5651

57-
fun generateFromId(discordId: ULong): UUID {
52+
fun discordToUuid(discordId: ULong): UUID {
5853
val mostSigBytes: ByteArray = ByteArray(8) { (discordId shr ((7 - it) * 8) and 0xFFu).toByte() }
5954
val leastSigBytes: ByteArray = mostSigBytes.map { it.inv() }.toByteArray()
6055

@@ -63,4 +58,11 @@ object SnowflakeToUUID {
6358

6459
return UUID(mostSigBits, leastSigBits)
6560
}
61+
62+
fun uuidToDiscord(uuid: UUID): ULong {
63+
val mostSigBits: Long = uuid.mostSignificantBits
64+
val mostSigBytes: ByteArray = ByteArray(8) { i -> ((mostSigBits shr ((7 - i) * 8)) and 0xFF).toByte() }
65+
66+
return mostSigBytes.fold(0u) { acc, byte -> (acc shl 8) or (byte.toULong() and 0xFFu) }
67+
}
6668
}

src/main/kotlin/ua/mei/minekord/bot/MinekordBot.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
package ua.mei.minekord.bot
22

33
import dev.kord.core.entity.Guild
4+
import dev.kord.core.entity.channel.TextChannel
45
import dev.kordex.core.ExtensibleBot
56
import kotlinx.coroutines.CoroutineScope
67
import kotlinx.coroutines.Dispatchers
78
import kotlinx.coroutines.launch
9+
import ua.mei.minekord.bot.extensions.IPCheckExtension
810
import ua.mei.minekord.config.BotSpec
911
import ua.mei.minekord.config.config
10-
import ua.mei.minekord.extensions.StartupExtension
12+
import ua.mei.minekord.bot.extensions.SetupExtension
1113
import kotlin.coroutines.CoroutineContext
1214

1315
object MinekordBot : CoroutineScope {
1416
lateinit var bot: ExtensibleBot
1517

1618
var guild: Guild? = null
19+
var chat: TextChannel? = null
1720

1821
fun launchBot() {
1922
launch {
2023
bot = ExtensibleBot(config[BotSpec.token]) {
2124
extensions {
22-
add(::StartupExtension)
25+
add(::SetupExtension)
26+
add(::IPCheckExtension)
2327
}
2428
}
2529

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package ua.mei.minekord.bot.extensions
2+
3+
import dev.kord.common.entity.ButtonStyle
4+
import dev.kord.common.entity.Snowflake
5+
import dev.kord.core.behavior.channel.createEmbed
6+
import dev.kord.core.behavior.channel.createMessage
7+
import dev.kord.core.entity.User
8+
import dev.kord.rest.builder.message.actionRow
9+
import dev.kord.rest.builder.message.embed
10+
import dev.kordex.core.components.components
11+
import dev.kordex.core.components.publicButton
12+
import dev.kordex.core.extensions.Extension
13+
import io.ktor.util.network.address
14+
import kotlinx.coroutines.launch
15+
import ua.mei.minekord.bot.DiscordUtils
16+
import ua.mei.minekord.bot.MinekordBot
17+
import ua.mei.minekord.cache.IPCache
18+
import ua.mei.minekord.event.IPCheckEvent
19+
20+
class IPCheckExtension : Extension() {
21+
override val name: String = "IP Check Extension"
22+
23+
override suspend fun setup() {
24+
IPCheckEvent.event.register { address, profile ->
25+
MinekordBot.launch {
26+
try {
27+
val discordId: ULong = DiscordUtils.uuidToDiscord(profile.id)
28+
val user: User? = kord.getUser(Snowflake(discordId))
29+
30+
user?.getDmChannelOrNull()?.createMessage {
31+
embed {
32+
title = "Join with new IP"
33+
description = "This is your IP?\n> ${address.address}"
34+
}
35+
components {
36+
publicButton {
37+
label = "Yes"
38+
style = ButtonStyle.Success
39+
40+
action {
41+
IPCache.putIntoCache(profile.id, address.address)
42+
respond {
43+
content = "ІДІ НАХУЙ"
44+
}
45+
}
46+
}
47+
publicButton {
48+
label = "No"
49+
style = ButtonStyle.Danger
50+
51+
action {
52+
respond {
53+
content = "ІДІ НАХУЙ"
54+
}
55+
}
56+
}
57+
}
58+
}
59+
} catch (_: Exception) {
60+
61+
}
62+
}
63+
}
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
package ua.mei.minekord.extensions
1+
package ua.mei.minekord.bot.extensions
22

33
import dev.kord.common.entity.Snowflake
4+
import dev.kord.core.entity.channel.TextChannel
45
import dev.kordex.core.extensions.Extension
56
import ua.mei.minekord.bot.MinekordBot
67
import ua.mei.minekord.config.BotSpec
78
import ua.mei.minekord.config.config
89

9-
class StartupExtension : Extension() {
10+
class SetupExtension : Extension() {
1011
override val name: String = "Startup"
1112

1213
override suspend fun setup() {
1314
MinekordBot.guild = kord.getGuildOrNull(Snowflake(config[BotSpec.guild]))
15+
MinekordBot.chat = MinekordBot.guild?.getChannel(Snowflake(config[BotSpec.chat])) as? TextChannel
1416
}
1517
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package ua.mei.minekord.cache
2+
3+
import com.google.gson.Gson
4+
import com.google.gson.GsonBuilder
5+
import com.google.gson.stream.JsonReader
6+
import net.fabricmc.loader.api.FabricLoader
7+
import java.io.FileReader
8+
import java.nio.file.Files
9+
import java.nio.file.Path
10+
import java.util.UUID
11+
12+
object IPCache {
13+
private var cache: MutableMap<UUID, String> = mutableMapOf()
14+
val path: Path = FabricLoader.getInstance().gameDir.resolve("ip-cache.json")
15+
val gson: Gson = GsonBuilder()
16+
.setPrettyPrinting()
17+
.create()
18+
19+
fun load() {
20+
if (!Files.exists(path)) {
21+
Files.createFile(path)
22+
Files.write(path, "{}".toByteArray())
23+
}
24+
val reader: JsonReader = JsonReader(FileReader(path.toFile()))
25+
cache = gson.fromJson(reader, MutableMap::class.java)
26+
reader.close()
27+
}
28+
29+
fun save() {
30+
if (!Files.exists(path)) {
31+
Files.createFile(path)
32+
}
33+
Files.write(path, gson.toJson(cache).toByteArray())
34+
}
35+
36+
fun putIntoCache(uuid: UUID, ip: String) {
37+
cache[uuid] = ip
38+
save()
39+
}
40+
41+
fun getFromCache(uuid: UUID): String {
42+
return cache[uuid] ?: ""
43+
}
44+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ua.mei.minekord.event
2+
3+
import com.mojang.authlib.GameProfile
4+
import net.fabricmc.fabric.api.event.Event
5+
import net.fabricmc.fabric.api.event.EventFactory
6+
import java.net.SocketAddress
7+
8+
fun interface IPCheckEvent {
9+
fun request(address: SocketAddress, profile: GameProfile)
10+
11+
companion object {
12+
val event: Event<IPCheckEvent> = EventFactory.createArrayBacked(IPCheckEvent::class.java) { listeners ->
13+
IPCheckEvent { address, profile ->
14+
listeners.forEach { listener ->
15+
listener.request(address, profile)
16+
}
17+
}
18+
}
19+
}
20+
}

src/main/resources/minekord.mixins.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"package": "ua.mei.minekord.mixin",
55
"compatibilityLevel": "JAVA_21",
66
"mixins": [
7+
"PlayerManagerMixin",
78
"ServerLoginNetworkHandlerMixin"
89
],
910
"injectors": {

0 commit comments

Comments
 (0)