finish client & server communicate

finish database
This commit is contained in:
TONY_All 2023-03-27 01:19:37 +08:00
parent aa4a229b7d
commit e1459c3e8e
23 changed files with 359 additions and 49 deletions

View File

@ -9,13 +9,13 @@ import java.util.List;
@SuppressWarnings("unused") // API
public interface MultiServerManAPI {
@NotNull
ServerInfo getServer(@NotNull String type, @NotNull List<ProxiedPlayer> players);
ServerInfo getServer(@NotNull String type, @NotNull List<String> players);
void informEnd(int id);
@NotNull
ServerInfo getPlayerServer(@NotNull ProxiedPlayer player);
ServerInfo getPlayerServer(@NotNull String player);
Boolean containPlayer(@NotNull ProxiedPlayer player);
Boolean containPlayer(@NotNull String player);
}

View File

@ -1,17 +1,22 @@
package cc.maxmc.msm.api;
@SuppressWarnings("unused") // API
public abstract class MultiServerManAPIProvider {
private static MultiServerManAPIProvider instance;
public class MultiServerManAPIProvider {
private static MultiServerManAPI instance = null;
public static MultiServerManAPI getAPI() {
if (instance == null) {
throw new NotLoadedException();
}
return instance.provideAPI();
return instance;
}
abstract MultiServerManAPI provideAPI();
public static void register(MultiServerManAPI api) {
if (instance != null) {
throw new IllegalStateException("The MultiServerMan API has been load twice");
}
instance = api;
}
/**
* 在加载 API 之前请求 API 时引发异常

View File

@ -1,5 +1,9 @@
package cc.maxmc.msm.child
import cc.maxmc.msm.api.MultiServerManAPIProvider
import cc.maxmc.msm.child.api.APIImpl
import cc.maxmc.msm.child.api.APIPacketListener
import cc.maxmc.msm.child.command.Api
import cc.maxmc.msm.child.command.Send
import cc.maxmc.msm.child.netty.NetClient
import cc.maxmc.msm.child.settings.Settings
@ -13,7 +17,10 @@ class MultiServerMan : Plugin() {
}
override fun onEnable() {
MultiServerManAPIProvider.register(APIImpl)
ProxyServer.getInstance().pluginManager.registerCommand(this, Send)
ProxyServer.getInstance().pluginManager.registerCommand(this, Api)
ProxyServer.getInstance().pluginManager.registerListener(this, APIPacketListener)
NetworkRegistry
NetClient.start(Settings.Parent.address, Settings.Parent.port)
}

View File

@ -4,44 +4,45 @@ import cc.maxmc.msm.api.MultiServerManAPI
import cc.maxmc.msm.api.misc.ServerInfo
import cc.maxmc.msm.child.netty.NetClient
import cc.maxmc.msm.common.network.packet.PPacketAPICall
import net.md_5.bungee.api.connection.ProxiedPlayer
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
@Suppress("UNCHECKED_CAST")
object APIImpl : MultiServerManAPI {
val apiCallCache = HashMap<UUID, CompletableFuture<out Any>>()
val apiCallCache = HashMap<UUID, CompletableFuture<in Any>>()
private const val TIMEOUT = 3L
override fun getServer(type: String, players: MutableList<ProxiedPlayer>): ServerInfo {
val packet = PPacketAPICall.PPacketCallGetServer(type, players.map { it.name })
override fun getServer(type: String, players: MutableList<String>): ServerInfo {
val packet = PPacketAPICall.PPacketCallGetServer(type, players)
NetClient.sendPacket(packet)
val future = CompletableFuture<ServerInfo>()
apiCallCache[UUID.randomUUID()] = future
return future.get(1, TimeUnit.SECONDS)
apiCallCache[packet.uid] = future as CompletableFuture<Any>
return future.get(TIMEOUT, TimeUnit.SECONDS)
}
override fun informEnd(id: Int) {
val packet = PPacketAPICall.PPacketCallInformEnd(id)
NetClient.sendPacket(packet)
val future = CompletableFuture<Unit>()
apiCallCache[UUID.randomUUID()] = future
future.get(1, TimeUnit.SECONDS)
apiCallCache[packet.uid] = future as CompletableFuture<Any>
future.get(TIMEOUT, TimeUnit.SECONDS)
return
}
override fun getPlayerServer(player: ProxiedPlayer): ServerInfo {
val packet = PPacketAPICall.PPacketCallGetPlayerServer(player.name)
override fun getPlayerServer(player: String): ServerInfo {
val packet = PPacketAPICall.PPacketCallGetPlayerServer(player)
NetClient.sendPacket(packet)
val future = CompletableFuture<ServerInfo>()
apiCallCache[UUID.randomUUID()] = future
return future.get(1, TimeUnit.SECONDS)
apiCallCache[packet.uid] = future as CompletableFuture<Any>
return future.get(TIMEOUT, TimeUnit.SECONDS)
}
override fun containPlayer(player: ProxiedPlayer): Boolean {
val packet = PPacketAPICall.PPacketCallContainPlayer(player.name)
override fun containPlayer(player: String): Boolean {
val packet = PPacketAPICall.PPacketCallContainPlayer(player)
NetClient.sendPacket(packet)
val future = CompletableFuture<Boolean>()
apiCallCache[UUID.randomUUID()] = future
return future.get(1, TimeUnit.SECONDS)
apiCallCache[packet.uid] = future as CompletableFuture<Any>
return future.get(TIMEOUT, TimeUnit.SECONDS)
}
}

View File

@ -0,0 +1,40 @@
package cc.maxmc.msm.child.api
import cc.maxmc.msm.common.event.PacketReceiveEvent
import cc.maxmc.msm.common.network.packet.CPacketAPICallback
import cc.maxmc.msm.common.utils.debug
import net.md_5.bungee.api.plugin.Listener
import net.md_5.bungee.event.EventHandler
object APIPacketListener : Listener {
@EventHandler
fun onPacket(packetEvent: PacketReceiveEvent) {
val packet = packetEvent.packet
if (packet !is CPacketAPICallback) {
return
}
val future = APIImpl.apiCallCache[packet.uid]
when (packet) {
is CPacketAPICallback.CPacketCallbackContainPlayer -> {
future?.complete(packet.value)
?: throw IllegalStateException("Packet callback received, however no request")
}
is CPacketAPICallback.CPacketCallbackGetPlayerServer -> {
future?.complete(packet.serverInfo)
?: throw IllegalStateException("Packet callback received, however no request")
}
is CPacketAPICallback.CPacketCallbackGetServer -> {
future?.complete(packet.serverInfo)
?: throw IllegalStateException("Packet callback received, however no request")
}
is CPacketAPICallback.CPacketCallbackInformEnd -> {
future?.complete(Unit)
?: throw IllegalStateException("Packet callback received, however no request")
}
}
}
}

View File

@ -0,0 +1,36 @@
package cc.maxmc.msm.child.command
import cc.maxmc.msm.api.MultiServerManAPIProvider
import net.md_5.bungee.api.CommandSender
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.api.plugin.Command
object Api : Command("api") {
override fun execute(sender: CommandSender, args: Array<out String>) {
sender.sendMessage(TextComponent("Calling API"))
val api = args[0]
val apiInst = MultiServerManAPIProvider.getAPI()
@Suppress("IMPLICIT_CAST_TO_ANY") val ret = when (api) {
"gs" -> {
apiInst.getServer("def", listOf("abc", "def"))
}
"ie" -> {
apiInst.informEnd(100)
}
"gps" -> {
apiInst.getPlayerServer("def")
}
"cp" -> {
apiInst.containPlayer("awa")
}
else -> {
"non api exist"
}
}.toString()
sender.sendMessage(TextComponent("Returning $ret"))
}
}

View File

@ -22,7 +22,12 @@ object NetClient {
.option(ChannelOption.TCP_NODELAY, true)
.remoteAddress(address, port)
.connect().addListener(ChannelFutureListener {
println("§a| §7成功连接到集群的主节点. (${it.channel().remoteAddress()})")
val result = it.cause() ?: return@ChannelFutureListener println(
"§a| §7成功连接到集群的主节点. (${
it.channel().remoteAddress()
})"
)
result.printStackTrace()
})
channel = future.channel()
}

View File

@ -6,6 +6,11 @@ object Settings {
val name
get() = config
val portRange
get() = config.getStringList("ports").flatMap {
it.toIntOrNull()?.run { listOf(this) } ?: it.split("..").run { this[0].toInt()..this[1].toInt() }
}
object Parent {
val address: String
get() = config.getString("parent.address", "localhost")

View File

@ -1,3 +1,7 @@
parent:
address: 127.0.0.1
port: 23333
ports:
- 30000
- 30001..30019
-

View File

@ -9,7 +9,7 @@ import io.netty.channel.SimpleChannelInboundHandler
@Sharable
object ClusterPacketHandler : SimpleChannelInboundHandler<BungeePacket>() {
override fun channelRead0(ctx: ChannelHandlerContext, msg: BungeePacket) {
debug("call event")
// debug("call event")
PacketReceiveEvent(ctx.channel(), msg).callEvent()
}
}

View File

@ -14,10 +14,8 @@ class ClusterMsgCodec(private val current: NetworkRegistry.PacketDirection) : By
}
override fun decode(ctx: ChannelHandlerContext, `in`: ByteBuf, out: MutableList<Any>) {
debug("decode packet")
val id = `in`.readInt()
val packet = NetworkRegistry.getPacketByID(current, id)
debug(packet.toString())
packet.decode(`in`)
out.add(packet)
}

View File

@ -1,7 +1,9 @@
package cc.maxmc.msm.common.network.netty
import cc.maxmc.msm.common.network.BungeePacket
import cc.maxmc.msm.common.network.packet.CPacketAPICallback
import cc.maxmc.msm.common.network.packet.CPacketDebug
import cc.maxmc.msm.common.network.packet.PPacketAPICall
import cc.maxmc.msm.common.network.packet.PPacketDebug
import cc.maxmc.msm.common.utils.debug
import com.google.common.collect.HashBiMap
@ -11,9 +13,16 @@ object NetworkRegistry {
private val childBoundMap = HashBiMap.create<Int, Class<out BungeePacket>>()
init {
debug("init registry")
registerPacket(PacketDirection.PARENT_BOUND, PPacketDebug::class.java)
registerPacket(PacketDirection.PARENT_BOUND, PPacketAPICall.PPacketCallGetPlayerServer::class.java)
registerPacket(PacketDirection.PARENT_BOUND, PPacketAPICall.PPacketCallContainPlayer::class.java)
registerPacket(PacketDirection.PARENT_BOUND, PPacketAPICall.PPacketCallInformEnd::class.java)
registerPacket(PacketDirection.PARENT_BOUND, PPacketAPICall.PPacketCallGetServer::class.java)
registerPacket(PacketDirection.CHILD_BOUND, CPacketDebug::class.java)
registerPacket(PacketDirection.CHILD_BOUND, CPacketAPICallback.CPacketCallbackGetPlayerServer::class.java)
registerPacket(PacketDirection.CHILD_BOUND, CPacketAPICallback.CPacketCallbackContainPlayer::class.java)
registerPacket(PacketDirection.CHILD_BOUND, CPacketAPICallback.CPacketCallbackInformEnd::class.java)
registerPacket(PacketDirection.CHILD_BOUND, CPacketAPICallback.CPacketCallbackGetServer::class.java)
}
private fun registerPacket(direction: PacketDirection, packet: Class<out BungeePacket>) {
@ -27,9 +36,7 @@ object NetworkRegistry {
}
fun getPacketID(side: PacketDirection, packet: BungeePacket): Int {
debug("start get PID")
val map = if (side == PacketDirection.PARENT_BOUND) childBoundMap else parentBoundMap
debug("map use $map")
return map.inverse()[packet::class.java] ?: throw IllegalStateException("Packet does not in registry.")
}

View File

@ -0,0 +1,72 @@
package cc.maxmc.msm.common.network.packet
import cc.maxmc.msm.api.misc.ServerInfo
import cc.maxmc.msm.common.network.BungeePacket
import cc.maxmc.msm.common.utils.readServerInfo
import cc.maxmc.msm.common.utils.writeServerInfo
import io.netty.buffer.ByteBuf
import net.md_5.bungee.protocol.DefinedPacket
import java.util.*
sealed class CPacketAPICallback(
var uid: UUID
) : BungeePacket() {
override fun encode(buf: ByteBuf) {
DefinedPacket.writeUUID(uid, buf)
}
override fun decode(buf: ByteBuf) {
uid = DefinedPacket.readUUID(buf)
}
class CPacketCallbackGetServer(
var serverInfo: ServerInfo = ServerInfo(),
uid: UUID = UUID.randomUUID()
) : CPacketAPICallback(uid = uid) {
override fun encode(buf: ByteBuf) {
super.encode(buf)
buf.writeServerInfo(serverInfo)
}
override fun decode(buf: ByteBuf) {
super.decode(buf)
serverInfo = buf.readServerInfo()
}
}
class CPacketCallbackInformEnd(
uid: UUID = UUID.randomUUID()
) : CPacketAPICallback(uid = uid)
class CPacketCallbackGetPlayerServer(
var serverInfo: ServerInfo,
uid: UUID = UUID.randomUUID()
) : CPacketAPICallback(uid = uid) {
override fun encode(buf: ByteBuf) {
super.encode(buf)
buf.writeServerInfo(serverInfo)
}
override fun decode(buf: ByteBuf) {
super.decode(buf)
serverInfo = buf.readServerInfo()
}
}
class CPacketCallbackContainPlayer(
var value: Boolean,
uid: UUID = UUID.randomUUID()
) : CPacketAPICallback(uid = uid) {
override fun encode(buf: ByteBuf) {
super.encode(buf)
buf.writeBoolean(value)
}
override fun decode(buf: ByteBuf) {
super.decode(buf)
value = buf.readBoolean()
}
}
}

View File

@ -4,11 +4,9 @@ import cc.maxmc.msm.common.network.BungeePacket
import io.netty.buffer.ByteBuf
data class CPacketDebug(
var content: String
var content: String = ""
) : BungeePacket() {
constructor() : this("")
override fun encode(buf: ByteBuf) {
val array = content.encodeToByteArray()
buf.writeInt(array.size)
@ -17,6 +15,8 @@ data class CPacketDebug(
override fun decode(buf: ByteBuf) {
val size = buf.readInt()
content = buf.readBytes(size).array().decodeToString()
val encoded = ByteArray(size)
buf.readBytes(encoded)
content = encoded.decodeToString()
}
}

View File

@ -1,7 +1,6 @@
package cc.maxmc.msm.common.network.packet
import cc.maxmc.msm.common.network.BungeePacket
import cc.maxmc.msm.common.utils.debug
import io.netty.buffer.ByteBuf
data class PPacketDebug(
@ -9,11 +8,9 @@ data class PPacketDebug(
) : BungeePacket() {
override fun encode(buf: ByteBuf) {
debug("start packet encode")
val array = content.encodeToByteArray()
buf.writeInt(array.size)
buf.writeBytes(array)
debug("end packet encode")
}
override fun decode(buf: ByteBuf) {

View File

@ -9,8 +9,8 @@ fun log(msg: String) {
val debug = true
fun debug(msg: String) {
if (debug) {
log("§fDEBUG | §7$msg")
}
}
//fun debug(msg: String) {
// if (debug) {
// log("§fDEBUG | §7$msg")
// }
//}

View File

@ -1,5 +1,7 @@
package cc.maxmc.msm.parent
import cc.maxmc.msm.api.MultiServerManAPIProvider
import cc.maxmc.msm.parent.api.APIImpl
import cc.maxmc.msm.parent.listener.PacketListener
import cc.maxmc.msm.parent.netty.NetManager
import net.md_5.bungee.api.ProxyServer
@ -9,6 +11,7 @@ class MultiServerMan : Plugin() {
override fun onEnable() {
instance = this
MultiServerManAPIProvider.register(APIImpl)
ProxyServer.getInstance().pluginManager.registerListener(this, PacketListener)
NetManager.startServer()
}

View File

@ -0,0 +1,27 @@
package cc.maxmc.msm.parent.api
import cc.maxmc.msm.api.MultiServerManAPI
import cc.maxmc.msm.api.misc.ServerInfo
import com.google.common.net.HostAndPort
object APIImpl : MultiServerManAPI {
override fun getServer(type: String, players: MutableList<String>): ServerInfo {
TODO("not implemented")
return ServerInfo(HostAndPort.fromString("127.0.0.1:23456"), 1024)
}
override fun informEnd(id: Int) {
TODO("not implemented")
return
}
override fun getPlayerServer(player: String): ServerInfo {
TODO("not implemented")
return ServerInfo(HostAndPort.fromString("127.0.0.1:34567"), 1025)
}
override fun containPlayer(player: String): Boolean {
TODO("not implemented")
return true
}
}

View File

@ -2,22 +2,84 @@ package cc.maxmc.msm.parent.database
import cc.maxmc.msm.parent.settings.Settings
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.pool.HikariPool
import java.sql.Timestamp
class SQLDatabase {
val config = HikariConfig()
private val config = HikariConfig()
private lateinit var pool: HikariPool
fun initDatabase() {
val db = Settings.Database
config.jdbcUrl = "jdbc:mysql://${db.address}:${db.port}"
config.username = db.username
config.password = db.password
config.schema = db.database
pool = HikariPool(config)
}
fun getMatch(id: Int) {
}
fun recordMatch() {
fun createTable() {
pool.connection.use {
it.createStatement().use { statement ->
statement.execute(
"""
CREATE TABLE `match`
(
`id` int auto_increment,
`start` datetime not null,
`end` datetime null,
`players` text not null ,
PRIMARY KEY (`id`)
)
""".trimIndent()
)
}
}
}
fun getPlayerMatch(player: String): Int {
pool.connection.use {
it.prepareStatement("select `id` from `match` where find_in_set(?, players) AND end IS NULL")
.use { prepared ->
prepared.setString(0, player)
val rs = prepared.executeQuery()
if (!rs.next()) {
return -1
}
return rs.getInt(1)
}
}
}
fun recordMatch(type: String, players: List<String>, start: Long = System.currentTimeMillis()) =
pool.connection.use {
val prepared = it.prepareStatement(
"""
insert into `match` (type, start, end, players)
values (?, ?, ?, ?);
""".trimIndent()
)
prepared.apply {
setString(1, type)
setTimestamp(2, Timestamp(start))
setTimestamp(3, null)
setString(4, players.joinToString(","))
}
prepared.execute()
prepared.close()
return@use it.createStatement().use { statement ->
val result = statement.executeQuery("select last_insert_id()")
result.next()
result.getInt(1)
}
}
fun endMatch(id: Int, end: Long = System.currentTimeMillis()) = pool.connection.use {
val prepare = it.prepareStatement("update `match` set end = ? where id = ?")
prepare.apply {
setTimestamp(1, Timestamp(end))
setInt(2, id)
}
prepare.execute()
prepare.close()
}
}

View File

@ -1,13 +1,44 @@
package cc.maxmc.msm.parent.listener
import cc.maxmc.msm.api.MultiServerManAPIProvider
import cc.maxmc.msm.common.event.PacketReceiveEvent
import cc.maxmc.msm.common.network.packet.CPacketAPICallback
import cc.maxmc.msm.common.network.packet.CPacketDebug
import cc.maxmc.msm.common.network.packet.PPacketAPICall
import cc.maxmc.msm.common.network.packet.PPacketDebug
import cc.maxmc.msm.common.utils.debug
import cc.maxmc.msm.common.utils.log
import net.md_5.bungee.api.plugin.Listener
import net.md_5.bungee.event.EventHandler
object PacketListener : Listener {
val api = MultiServerManAPIProvider.getAPI()
@EventHandler
fun onAPICall(evt: PacketReceiveEvent) {
val packet = evt.packet
if (packet !is PPacketAPICall) return
val callback = when (packet) {
is PPacketAPICall.PPacketCallContainPlayer -> {
CPacketAPICallback.CPacketCallbackContainPlayer(api.containPlayer(packet.player), packet.uid)
}
is PPacketAPICall.PPacketCallGetPlayerServer -> {
CPacketAPICallback.CPacketCallbackGetPlayerServer(api.getPlayerServer(packet.player), packet.uid)
}
is PPacketAPICall.PPacketCallGetServer -> {
CPacketAPICallback.CPacketCallbackGetServer(api.getServer(packet.type, packet.players), packet.uid)
}
is PPacketAPICall.PPacketCallInformEnd -> {
api.informEnd(packet.matchID)
CPacketAPICallback.CPacketCallbackInformEnd(packet.uid)
}
}
evt.channel.writeAndFlush(callback)
}
@EventHandler
fun onPacket(evt: PacketReceiveEvent) {
val packet = evt.packet

View File

@ -6,6 +6,6 @@ object ChildManager {
val children = ArrayList<ChildBungee>()
fun registerChild(child: ChildBungee) {
children.add(child)
}
}

View File

@ -1,5 +1,10 @@
package cc.maxmc.msm.parent.misc
import cc.maxmc.msm.common.network.BungeePacket
import io.netty.channel.Channel
data class ChildBungee(val channel: Channel)
class ChildBungee(val channel: Channel, var ports: List<Int>, var usedPorts: List<Int> = ArrayList()) {
fun sendPacket(packet: BungeePacket) {
channel.writeAndFlush(packet)
}
}

View File

@ -20,7 +20,12 @@ object NetManager {
.childHandler(pipelineInit(NetworkRegistry.PacketDirection.PARENT_BOUND))
.bind(Settings.serverPort)
.addListener(ChannelFutureListener {
log("§a| §7集群主服务端启动成功. ${it.channel().localAddress()}")
val result = it.cause() ?: return@ChannelFutureListener log(
"§a| §7集群主服务端启动成功. ${
it.channel().localAddress()
}"
)
result.printStackTrace()
})
}