diff --git a/README.md b/README.md index f30eaa4..0eaeb3f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ ### `func getServer()` -- 接口描述:获取一个可用的服务器ip,并记录该对局的参与玩家。 +- 接口描述:提供模版类型,获取一个可用的服务器ip,并记录该对局的参与玩家。 - 返回值: @@ -38,7 +38,8 @@ - Int类型的对局id - 参数列表: - - `ArrayList players` + - `(List players)` + - ### `func informEnd()` @@ -47,7 +48,7 @@ - 返回值:无 - 参数列表: - - int id + - `(int id)` ### `func getPlayerServer()` @@ -60,12 +61,52 @@ - 参数列表: - `(Player player)` -### `func containPlayer` +### `func containPlayer()` - 接口描述:查询某个玩家是否在对局中。 - - 返回值: - bool - - 参数列表: - - `(Player player)` \ No newline at end of file + - `(Player player)` + +## 数据库结构 + +- `match` + +| id(int) | start(datetime) | end(datetime) | players(text) | +| ------- | --------------- | ------------- | ------------------------------------ | +| 1 | 1678554776 | 1678554784 | TONY_All,Sanseyooyea,PlayerA,PlayerB | +## API 使用 + +依赖: + +```kotlin +dependencies { + compileOnly("cc.maxmc.msm:MultiServerMan-API:${version}") +} +``` + +使用: + +```java +List players; + +MultiServerManAPI api = MultiServerManAPIProvider.getAPI(); +api.getServer("default", players); +``` + +## Q&A + +1. Q: 不同类型的服务端是怎么区分的 + + A: 每个子端保留多种模版 + +2. Q: 数据库类型 + + A: MySQL / MariaDB + +3. Q: getServer是需要负责把玩家传到子服还是只是提供服务器并记录玩家 + + A: 只提供服务器并记录玩家 + +4. Q: 服务端是 \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts new file mode 100644 index 0000000..d9a081c --- /dev/null +++ b/api/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("jvm") +} + +group = "cc.maxmc.msm.api" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(kotlin("stdlib")) + @Suppress("VulnerableLibrariesLocal") + compileOnly("io.github.waterfallmc:waterfall-api:1.19-R0.1-SNAPSHOT") +} \ No newline at end of file diff --git a/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPI.java b/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPI.java new file mode 100644 index 0000000..add290c --- /dev/null +++ b/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPI.java @@ -0,0 +1,18 @@ +package cc.maxmc.msm.api; + +import cc.maxmc.msm.api.misc.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.List; + +@SuppressWarnings("unused") // API +public interface MultiServerManAPI { + ServerInfo getServer(String type, List players); + + void informEnd(int id); + + ServerInfo getPlayerServer(ProxiedPlayer player); + + Boolean containPlayer(ProxiedPlayer player); + +} diff --git a/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPIProvider.java b/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPIProvider.java new file mode 100644 index 0000000..729622e --- /dev/null +++ b/api/src/main/java/cc/maxmc/msm/api/MultiServerManAPIProvider.java @@ -0,0 +1,31 @@ +package cc.maxmc.msm.api; + +@SuppressWarnings("unused") // API +public abstract class MultiServerManAPIProvider { + private static MultiServerManAPIProvider instance; + + public static MultiServerManAPI getAPI() { + if (instance == null) { + throw new NotLoadedException(); + } + return instance.provideAPI(); + } + + abstract MultiServerManAPI provideAPI(); + + /** + * 在加载 API 之前请求 API 时引发异常。 + */ + private static final class NotLoadedException extends IllegalStateException { + private static final String MESSAGE = "The MultiServerMan API isn't loaded yet!\n" + + "This could be because:\n" + + " a) the MultiServerMan plugin is not installed or it failed to enable\n" + + " b) the plugin in the stacktrace does not declare a dependency on MultiServerMan\n" + + " c) the plugin in the stacktrace is retrieving the API before the plugin 'enable' phase\n" + + " (call the #get method in onEnable, not the constructor!)\n"; + + NotLoadedException() { + super(MESSAGE); + } + } +} diff --git a/api/src/main/java/cc/maxmc/msm/api/misc/ServerInfo.java b/api/src/main/java/cc/maxmc/msm/api/misc/ServerInfo.java new file mode 100644 index 0000000..774a404 --- /dev/null +++ b/api/src/main/java/cc/maxmc/msm/api/misc/ServerInfo.java @@ -0,0 +1,25 @@ +package cc.maxmc.msm.api.misc; + +import com.google.common.net.HostAndPort; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("unused") // API +public class ServerInfo { + @Nullable + private final HostAndPort server; + private final int id; + + public ServerInfo(@Nullable HostAndPort server, int id) { + this.server = server; + this.id = id; + } + + @Nullable + public HostAndPort getServer() { + return server; + } + + public int getId() { + return id; + } +} diff --git a/build.gradle.kts b/build.gradle.kts index a909460..4be53d7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,27 +1,2 @@ -plugins { - kotlin("jvm") version "1.8.0" - application -} - group = "cc.maxmc.msm" -version = "1.0-SNAPSHOT" - -repositories { - mavenCentral() -} - -dependencies { - testImplementation(kotlin("test")) -} - -tasks.test { - useJUnitPlatform() -} - -kotlin { - jvmToolchain(8) -} - -application { - mainClass.set("MainKt") -} \ No newline at end of file +version = "1.0-SNAPSHOT" \ No newline at end of file diff --git a/child-side/build.gradle.kts b/child-side/build.gradle.kts new file mode 100644 index 0000000..0d17a48 --- /dev/null +++ b/child-side/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + kotlin("jvm") +} + +group = "cc.maxmc.msm.child" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(kotlin("stdlib")) + compileOnly("io.github.waterfallmc:waterfall-api:1.19-R0.1-SNAPSHOT") +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000..c754beb --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("jvm") +} + +group = "cc.maxmc.msm.common" + +repositories { + mavenCentral() + mavenLocal() + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(kotlin("stdlib")) + compileOnly("io.github.waterfallmc:waterfall-api:1.19-R0.1-SNAPSHOT") + compileOnly("net.md-5:bungeecord-proxy:1.19-R0.1-SNAPSHOT") { + isTransitive = false + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/cc/maxmc/msm/common/PacketInjector.kt b/common/src/main/kotlin/cc/maxmc/msm/common/PacketInjector.kt new file mode 100644 index 0000000..248c5da --- /dev/null +++ b/common/src/main/kotlin/cc/maxmc/msm/common/PacketInjector.kt @@ -0,0 +1,25 @@ +package cc.maxmc.msm.common + +import cc.maxmc.msm.common.network.ClusterMsgCodec +import io.netty.channel.Channel +import net.md_5.bungee.BungeeCord + +object PacketInjector { + private val bungee = BungeeCord.getInstance() + private val field = bungee::class.java.getDeclaredField("listeners").also { + it.isAccessible = true + } + + fun inject() { + val channel = getChannel() + channel.pipeline().addFirst("ClusterMsgCodec", ClusterMsgCodec) + } + + private fun getChannel(): Channel { + @Suppress("UNCHECKED_CAST") + val channelList = field.get(bungee) as Collection + while (true) { + return channelList.firstOrNull() ?: continue + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/cc/maxmc/msm/common/network/BungeePacket.kt b/common/src/main/kotlin/cc/maxmc/msm/common/network/BungeePacket.kt new file mode 100644 index 0000000..7c8ad02 --- /dev/null +++ b/common/src/main/kotlin/cc/maxmc/msm/common/network/BungeePacket.kt @@ -0,0 +1,9 @@ +package cc.maxmc.msm.common.network + +import io.netty.buffer.ByteBuf + +interface BungeePacket: Cloneable { + fun encode(buf: ByteBuf) + + fun decode(buf: ByteBuf) +} \ No newline at end of file diff --git a/common/src/main/kotlin/cc/maxmc/msm/common/network/ClusterMsgCodec.kt b/common/src/main/kotlin/cc/maxmc/msm/common/network/ClusterMsgCodec.kt new file mode 100644 index 0000000..67cacac --- /dev/null +++ b/common/src/main/kotlin/cc/maxmc/msm/common/network/ClusterMsgCodec.kt @@ -0,0 +1,15 @@ +package cc.maxmc.msm.common.network + +import io.netty.buffer.ByteBuf +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.ByteToMessageCodec + +object ClusterMsgCodec : ByteToMessageCodec() { + override fun encode(ctx: ChannelHandlerContext, msg: BungeePacket, out: ByteBuf) { + msg.encode(out) + } + + override fun decode(ctx: ChannelHandlerContext, `in`: ByteBuf, out: MutableList) { + + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/cc/maxmc/msm/common/network/NetworkRegistry.kt b/common/src/main/kotlin/cc/maxmc/msm/common/network/NetworkRegistry.kt new file mode 100644 index 0000000..4fb23e8 --- /dev/null +++ b/common/src/main/kotlin/cc/maxmc/msm/common/network/NetworkRegistry.kt @@ -0,0 +1,23 @@ +package cc.maxmc.msm.common.network + +import com.google.common.collect.HashBiMap + +object NetworkRegistry { + val parentBoundMap = HashBiMap.create() + val childBoundMap = HashBiMap.create() + + fun registerPacket(packet: BungeePacket, direction: PacketDirection) { + if (direction == PacketDirection.ParentBound) { + parentBoundMap + } else { + childBoundMap + }.let { + it[it.size] = packet + } + } + + enum class PacketDirection { + ParentBound, + ChildBound; + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 60c76b3..ef62641 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/parent-side/build.gradle.kts b/parent-side/build.gradle.kts new file mode 100644 index 0000000..4e97d48 --- /dev/null +++ b/parent-side/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + kotlin("jvm") + id("com.github.johnrengelman.shadow") +} + +group = "cc.maxmc.msm.parent" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":api")) + implementation(project(":common")) + @Suppress("VulnerableLibrariesLocal") + compileOnly("io.github.waterfallmc:waterfall-api:1.19-R0.1-SNAPSHOT") +} + +tasks.shadowJar { + relocate("kotlin", "cc.maxmc.msm.lib.kotlin") +} + +tasks.build { + dependsOn(tasks.shadowJar) +} \ No newline at end of file diff --git a/parent-side/src/main/kotlin/cc/maxmc/msm/parent/MultiServerMan.kt b/parent-side/src/main/kotlin/cc/maxmc/msm/parent/MultiServerMan.kt new file mode 100644 index 0000000..3334200 --- /dev/null +++ b/parent-side/src/main/kotlin/cc/maxmc/msm/parent/MultiServerMan.kt @@ -0,0 +1,21 @@ +package cc.maxmc.msm.parent + +import cc.maxmc.msm.common.PacketInjector +import net.md_5.bungee.api.ProxyServer +import net.md_5.bungee.api.plugin.Plugin + +class MultiServerMan: Plugin() { + + override fun onEnable() { + Thread.sleep(5000) + ProxyServer.getInstance().scheduler.runAsync(this) { + println("injecting") + PacketInjector.inject() + } + } + + override fun onDisable() { + + } + +} \ No newline at end of file diff --git a/parent-side/src/main/resources/bungee.yml b/parent-side/src/main/resources/bungee.yml new file mode 100644 index 0000000..990fd09 --- /dev/null +++ b/parent-side/src/main/resources/bungee.yml @@ -0,0 +1,3 @@ +name: MultiServerMan-Parent +main: cc.maxmc.msm.parent.MultiServerMan +author: TONY_All \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 21354ca..e5b0637 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,9 @@ +plugins { + id("com.github.johnrengelman.shadow") version "8.1.0" apply false + kotlin("jvm") version "1.8.10" apply false +} rootProject.name = "MultiServerMan" +include("common", "parent-side", "child-side") +include("api") \ No newline at end of file