mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 06:28:28 +08:00
Added ship servers and ship selection. Simplified configuration startup phase.
This commit is contained in:
parent
810c1cb549
commit
c627b33a51
@ -8,8 +8,8 @@ the `--config=/path/to/config.json` parameter to specify a configuration file.
|
||||
## Proxy
|
||||
|
||||
Phantasmal PSO server can proxy any other PSO server. Below is a sample configuration for proxying a
|
||||
locally running Tethealla patch and login server using the standard Tethealla client. Be sure to
|
||||
modify tethealla.ini and set server port to 22000.
|
||||
locally running Tethealla server using the standard Tethealla client. Be sure to modify
|
||||
tethealla.ini and set server port to 22000.
|
||||
|
||||
```json
|
||||
{
|
||||
@ -40,6 +40,24 @@ modify tethealla.ini and set server port to 22000.
|
||||
"version": "BB",
|
||||
"bindPort": 12001,
|
||||
"remotePort": 22001
|
||||
},
|
||||
{
|
||||
"name": "ship_proxy",
|
||||
"version": "BB",
|
||||
"bindPort": 13000,
|
||||
"remotePort": 5278
|
||||
},
|
||||
{
|
||||
"name": "block_1_proxy",
|
||||
"version": "BB",
|
||||
"bindPort": 13001,
|
||||
"remotePort": 5279
|
||||
},
|
||||
{
|
||||
"name": "block_2_proxy",
|
||||
"version": "BB",
|
||||
"bindPort": 13002,
|
||||
"remotePort": 5280
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2,6 +2,15 @@ package world.phantasmal.psoserv
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
val DEFAULT_CONFIG: Config = Config(
|
||||
address = null,
|
||||
patch = PatchServerConfig(),
|
||||
auth = ServerConfig(),
|
||||
account = ServerConfig(),
|
||||
proxy = null,
|
||||
ships = listOf(ShipServerConfig()),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Config(
|
||||
val address: String? = null,
|
||||
@ -9,6 +18,7 @@ class Config(
|
||||
val auth: ServerConfig? = null,
|
||||
val account: ServerConfig? = null,
|
||||
val proxy: ProxyConfig? = null,
|
||||
val ships: List<ShipServerConfig> = emptyList(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@ -18,6 +28,15 @@ class ServerConfig(
|
||||
val port: Int? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class ShipServerConfig(
|
||||
val run: Boolean = true,
|
||||
val name: String? = null,
|
||||
val uiName: String? = null,
|
||||
val address: String? = null,
|
||||
val port: Int? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class PatchServerConfig(
|
||||
val run: Boolean = true,
|
||||
@ -36,6 +55,7 @@ class ProxyConfig(
|
||||
|
||||
@Serializable
|
||||
class ProxyServerConfig(
|
||||
val run: Boolean = true,
|
||||
val name: String? = null,
|
||||
val version: GameVersionConfig,
|
||||
val bindAddress: String? = null,
|
||||
|
@ -19,51 +19,61 @@ private val DEFAULT_ADDRESS: Inet4Address = inet4Loopback()
|
||||
private const val DEFAULT_PATCH_PORT: Int = 11_000
|
||||
private const val DEFAULT_LOGIN_PORT: Int = 12_000
|
||||
private const val DEFAULT_ACCOUNT_PORT: Int = 12_001
|
||||
private const val DEFAULT_FIRST_SHIP_PORT: Int = 12_010
|
||||
|
||||
private val LOGGER = KotlinLogging.logger("main")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
LOGGER.info { "Initializing." }
|
||||
try {
|
||||
LOGGER.info { "Initializing." }
|
||||
|
||||
var configFile: File? = null
|
||||
// Try to get config file location from arguments first.
|
||||
var configFile: File? = null
|
||||
|
||||
for (arg in args) {
|
||||
val split = arg.split('=')
|
||||
// Parse arguments.
|
||||
for (arg in args) {
|
||||
val split = arg.split('=')
|
||||
|
||||
if (split.size == 2) {
|
||||
val (param, value) = split
|
||||
if (split.size == 2) {
|
||||
val (param, value) = split
|
||||
|
||||
when (param) {
|
||||
"--config" -> {
|
||||
configFile = File(value)
|
||||
when (param) {
|
||||
"--config" -> {
|
||||
configFile = File(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configFile == null) {
|
||||
configFile = File("config.json").takeIf { it.isFile }
|
||||
}
|
||||
|
||||
val config: Config
|
||||
|
||||
if (configFile != null) {
|
||||
LOGGER.info { "Using configuration file $configFile." }
|
||||
|
||||
val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
// Try default config file location if no file specified with --config argument.
|
||||
if (configFile == null) {
|
||||
configFile = File("config.json").takeIf { it.isFile }
|
||||
}
|
||||
|
||||
config = json.decodeFromString(configFile.readText())
|
||||
} else {
|
||||
config = Config()
|
||||
val config: Config
|
||||
|
||||
// Parse the config file if we found one, otherwise use default config.
|
||||
if (configFile != null) {
|
||||
LOGGER.info { "Using configuration file $configFile." }
|
||||
|
||||
val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
config = json.decodeFromString(configFile.readText())
|
||||
} else {
|
||||
config = DEFAULT_CONFIG
|
||||
}
|
||||
|
||||
// Initialize and start the server.
|
||||
val server = initialize(config)
|
||||
|
||||
LOGGER.info { "Starting up." }
|
||||
|
||||
server.start()
|
||||
} catch (e: Throwable) {
|
||||
LOGGER.error(e) { "Failed to start up." }
|
||||
}
|
||||
|
||||
val server = initialize(config)
|
||||
|
||||
LOGGER.info { "Starting up." }
|
||||
|
||||
server.start()
|
||||
}
|
||||
|
||||
private class PhantasmalServer(
|
||||
@ -87,39 +97,53 @@ private fun initialize(config: Config): PhantasmalServer {
|
||||
val accountAddress = config.account?.address?.let(::inet4Address) ?: defaultAddress
|
||||
val accountPort = config.account?.port ?: DEFAULT_ACCOUNT_PORT
|
||||
|
||||
var shipI = 1
|
||||
var shipPort = DEFAULT_FIRST_SHIP_PORT
|
||||
|
||||
val ships = config.ships.filter { it.run }.map { shipCfg ->
|
||||
val ship = ShipInfo(
|
||||
name = shipCfg.name ?: "ship_$shipI",
|
||||
uiName = shipCfg.uiName ?: "Ship $shipI",
|
||||
bindPair = Inet4Pair(
|
||||
shipCfg.address?.let(::inet4Address) ?: defaultAddress,
|
||||
shipCfg.port ?: shipPort++,
|
||||
)
|
||||
)
|
||||
shipI++
|
||||
ship
|
||||
}
|
||||
|
||||
val servers = mutableListOf<Server>()
|
||||
|
||||
// If no proxy config is specified, we run a regular PSO server by default.
|
||||
val run = config.proxy == null || !config.proxy.run
|
||||
|
||||
if (config.patch == null && run || config.patch?.run == true) {
|
||||
if (config.patch?.run == true) {
|
||||
val bindPair = Inet4Pair(
|
||||
config.patch?.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.patch?.port ?: DEFAULT_PATCH_PORT,
|
||||
config.patch.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.patch.port ?: DEFAULT_PATCH_PORT,
|
||||
)
|
||||
|
||||
LOGGER.info { "Configuring patch server to bind to $bindPair." }
|
||||
|
||||
servers.add(
|
||||
PatchServer(
|
||||
name = "patch",
|
||||
bindPair,
|
||||
welcomeMessage = config.patch?.welcomeMessage ?: "Welcome to Phantasmal World.",
|
||||
welcomeMessage = config.patch.welcomeMessage ?: "Welcome to Phantasmal World.",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (config.auth == null && run || config.auth?.run == true) {
|
||||
if (config.auth?.run == true) {
|
||||
val bindPair = Inet4Pair(
|
||||
config.auth?.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.auth?.port ?: DEFAULT_LOGIN_PORT,
|
||||
config.auth.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.auth.port ?: DEFAULT_LOGIN_PORT,
|
||||
)
|
||||
|
||||
LOGGER.info { "Configuring auth server to bind to $bindPair." }
|
||||
LOGGER.info {
|
||||
"Auth server will redirect to account server at $accountAddress:$accountPort."
|
||||
}
|
||||
|
||||
servers.add(
|
||||
AuthServer(
|
||||
name = "auth",
|
||||
bindPair,
|
||||
accountServerAddress = accountAddress,
|
||||
accountServerPort = accountPort,
|
||||
@ -127,18 +151,37 @@ private fun initialize(config: Config): PhantasmalServer {
|
||||
)
|
||||
}
|
||||
|
||||
if (config.account == null && run || config.account?.run == true) {
|
||||
if (config.account?.run == true) {
|
||||
val bindPair = Inet4Pair(
|
||||
config.account?.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.account?.port ?: DEFAULT_ACCOUNT_PORT,
|
||||
config.account.address?.let(::inet4Address) ?: defaultAddress,
|
||||
config.account.port ?: DEFAULT_ACCOUNT_PORT,
|
||||
)
|
||||
|
||||
LOGGER.info { "Configuring account server to bind to $bindPair." }
|
||||
LOGGER.info {
|
||||
"Account server will redirect to ${ships.size} ship servers: ${
|
||||
ships.joinToString { """"${it.name}" (${it.bindPair})""" }
|
||||
}."
|
||||
}
|
||||
|
||||
servers.add(
|
||||
AccountServer(
|
||||
name = "account",
|
||||
bindPair,
|
||||
ships,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
for (ship in ships) {
|
||||
LOGGER.info {
|
||||
"""Configuring ship server ${ship.name} ("${ship.uiName}") to bind to ${ship.bindPair}."""
|
||||
}
|
||||
|
||||
servers.add(
|
||||
ShipServer(
|
||||
ship.name,
|
||||
ship.bindPair,
|
||||
ship.uiName,
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -160,6 +203,10 @@ private fun initializeProxy(config: ProxyConfig): List<ProxyServer> {
|
||||
var nameI = 1
|
||||
|
||||
for (psc in config.servers) {
|
||||
if (!psc.run) {
|
||||
continue
|
||||
}
|
||||
|
||||
val name = psc.name ?: "proxy_${nameI++}"
|
||||
val bindPair = Inet4Pair(
|
||||
psc.bindAddress?.let(::inet4Address) ?: defaultBindAddress,
|
||||
|
@ -27,8 +27,12 @@ object BbMessageDescriptor : MessageDescriptor<BbMessage> {
|
||||
// Sorted by low-order byte, then high-order byte.
|
||||
0x0003 -> BbMessage.InitEncryption(buffer)
|
||||
0x0005 -> BbMessage.Disconnect(buffer)
|
||||
0x0007 -> BbMessage.BlockList(buffer)
|
||||
0x0010 -> BbMessage.MenuSelect(buffer)
|
||||
0x0019 -> BbMessage.Redirect(buffer)
|
||||
0x0083 -> BbMessage.LobbyList(buffer)
|
||||
0x0093 -> BbMessage.Authenticate(buffer)
|
||||
0x00A0 -> BbMessage.ShipList(buffer)
|
||||
0x01DC -> BbMessage.GuildCardHeader(buffer)
|
||||
0x02DC -> BbMessage.GuildCardChunk(buffer)
|
||||
0x03DC -> BbMessage.GetGuildCardChunk(buffer)
|
||||
@ -76,6 +80,40 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
constructor() : this(buf(0x0005))
|
||||
}
|
||||
|
||||
class BlockList(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(shipName: String, blocks: List<String>) : this(
|
||||
buf(0x0007, (blocks.size + 1) * 44, flags = blocks.size) {
|
||||
var index = 0
|
||||
writeInt(0x00040000) // Menu type.
|
||||
writeInt(index++)
|
||||
writeShort(0) // Flags.
|
||||
writeStringUtf16(shipName, byteLength = 34)
|
||||
|
||||
for (ship in blocks) {
|
||||
writeInt(MenuType.Block.toInt())
|
||||
writeInt(index++)
|
||||
writeShort(0) // Flags.
|
||||
writeStringUtf16(ship, byteLength = 34)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class MenuSelect(buffer: Buffer) : BbMessage(buffer) {
|
||||
val menuType: MenuType get() = MenuType.fromInt(int(0))
|
||||
val itemNo: Int get() = int(4)
|
||||
|
||||
constructor(menuType: MenuType, itemNo: Int) : this(
|
||||
buf(0x0010, 8) {
|
||||
writeInt(menuType.toInt())
|
||||
writeInt(itemNo)
|
||||
}
|
||||
)
|
||||
|
||||
override fun toString(): String =
|
||||
messageString("menuType" to menuType, "itemNo" to itemNo)
|
||||
}
|
||||
|
||||
class Redirect(buffer: Buffer) : BbMessage(buffer), RedirectMessage {
|
||||
override var ipAddress: ByteArray
|
||||
get() = byteArray(0, size = 4)
|
||||
@ -108,8 +146,23 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
)
|
||||
}
|
||||
|
||||
class LobbyList(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor() : this(
|
||||
buf(0x0083, 192) {
|
||||
repeat(15) {
|
||||
writeInt(MenuType.Lobby.toInt())
|
||||
writeInt(it + 1) // Item no.
|
||||
writeInt(0) // Padding.
|
||||
}
|
||||
// 12 zero bytes of padding.
|
||||
writeInt(0)
|
||||
writeInt(0)
|
||||
writeInt(0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 0x0093
|
||||
// Also contains ignored tag, hardware info and security data.
|
||||
class Authenticate(buffer: Buffer) : BbMessage(buffer) {
|
||||
val guildCard: Int get() = int(4)
|
||||
val version: Short get() = short(8)
|
||||
@ -118,6 +171,28 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
get() = stringAscii(offset = 20, maxByteLength = 16, nullTerminated = true)
|
||||
val password: String
|
||||
get() = stringAscii(offset = 68, maxByteLength = 16, nullTerminated = true)
|
||||
val magic: Int get() = int(132) // Should be 0xDEADBEEF
|
||||
val charSlot: Int get() = byte(136).toInt()
|
||||
val charSelected: Boolean get() = byte(137).toInt() != 0
|
||||
}
|
||||
|
||||
class ShipList(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(ships: List<String>) : this(
|
||||
buf(0x00A0, (ships.size + 1) * 44, flags = ships.size) {
|
||||
var index = 0
|
||||
writeInt(MenuType.Ship.toInt())
|
||||
writeInt(index++)
|
||||
writeShort(4) // Flags
|
||||
writeStringUtf16("SHIP/US", byteLength = 34)
|
||||
|
||||
for (ship in ships) {
|
||||
writeInt(MenuType.Ship.toInt())
|
||||
writeInt(index++)
|
||||
writeShort(0) // Flags
|
||||
writeStringUtf16(ship, byteLength = 34)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class GuildCardHeader(buffer: Buffer) : BbMessage(buffer) {
|
||||
@ -183,21 +258,21 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
// 0x00E3
|
||||
class CharSelect(buffer: Buffer) : BbMessage(buffer) {
|
||||
val slot: Int get() = uByte(0).toInt()
|
||||
val select: Boolean get() = byte(4).toInt() != 0
|
||||
val selected: Boolean get() = byte(4).toInt() != 0
|
||||
|
||||
override fun toString(): String =
|
||||
messageString("slot" to slot, "select" to select)
|
||||
messageString("slot" to slot, "select" to selected)
|
||||
}
|
||||
|
||||
class CharSelectAck(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(slot: Int, status: BbCharSelectStatus) : this(
|
||||
constructor(slot: Int, status: CharSelectStatus) : this(
|
||||
buf(0x00E4, 8) {
|
||||
writeInt(slot)
|
||||
writeInt(
|
||||
when (status) {
|
||||
BbCharSelectStatus.Update -> 0
|
||||
BbCharSelectStatus.Select -> 1
|
||||
BbCharSelectStatus.Nonexistent -> 2
|
||||
CharSelectStatus.Update -> 0
|
||||
CharSelectStatus.Select -> 1
|
||||
CharSelectStatus.Nonexistent -> 2
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -239,7 +314,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
|
||||
class AuthData(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(
|
||||
status: BbAuthStatus,
|
||||
status: AuthStatus,
|
||||
guildCard: Int,
|
||||
teamId: Int,
|
||||
slot: Int,
|
||||
@ -248,16 +323,16 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
buf(0x00E6, 60) {
|
||||
writeInt(
|
||||
when (status) {
|
||||
BbAuthStatus.Success -> 0
|
||||
BbAuthStatus.Error -> 1
|
||||
BbAuthStatus.Nonexistent -> 8
|
||||
AuthStatus.Success -> 0
|
||||
AuthStatus.Error -> 1
|
||||
AuthStatus.Nonexistent -> 8
|
||||
}
|
||||
)
|
||||
writeInt(0x10000)
|
||||
writeInt(guildCard)
|
||||
writeInt(teamId)
|
||||
writeInt(
|
||||
if (status == BbAuthStatus.Success) (0xDEADBEEF).toInt() else 0
|
||||
if (status == AuthStatus.Success) (0xDEADBEEF).toInt() else 0
|
||||
)
|
||||
writeByte(slot.toByte())
|
||||
writeByte(if (selected) 1 else 0)
|
||||
@ -351,7 +426,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
}
|
||||
}
|
||||
|
||||
enum class BbAuthStatus {
|
||||
enum class AuthStatus {
|
||||
Success, Error, Nonexistent
|
||||
}
|
||||
|
||||
@ -399,6 +474,36 @@ class FileListEntry(
|
||||
val filename: String,
|
||||
)
|
||||
|
||||
enum class BbCharSelectStatus {
|
||||
enum class CharSelectStatus {
|
||||
Update, Select, Nonexistent
|
||||
}
|
||||
|
||||
enum class MenuType(private val type: Int) {
|
||||
Lobby(-1),
|
||||
InfoDesk(0),
|
||||
Block(1),
|
||||
Game(2),
|
||||
QuestCategory(3),
|
||||
Quest(4),
|
||||
Ship(5),
|
||||
GameType(6),
|
||||
Gm(7),
|
||||
Unknown(Int.MIN_VALUE);
|
||||
|
||||
fun toInt(): Int = type
|
||||
|
||||
companion object {
|
||||
fun fromInt(type: Int): MenuType = when (type) {
|
||||
-1 -> Lobby
|
||||
0 -> InfoDesk
|
||||
1 -> Block
|
||||
2 -> Game
|
||||
3 -> QuestCategory
|
||||
4 -> Quest
|
||||
5 -> Ship
|
||||
6 -> GameType
|
||||
7 -> Gm
|
||||
else -> Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import world.phantasmal.psoserv.messages.*
|
||||
import world.phantasmal.psoserv.utils.crc32Checksum
|
||||
|
||||
class AccountServer(
|
||||
name: String,
|
||||
bindPair: Inet4Pair,
|
||||
) : GameServer<BbMessage>(name, bindPair) {
|
||||
private val ships: List<ShipInfo>,
|
||||
) : GameServer<BbMessage>("account", bindPair) {
|
||||
|
||||
override val messageDescriptor = BbMessageDescriptor
|
||||
|
||||
@ -48,7 +48,7 @@ class AccountServer(
|
||||
teamId = message.teamId
|
||||
send(
|
||||
BbMessage.AuthData(
|
||||
BbAuthStatus.Success,
|
||||
AuthStatus.Success,
|
||||
guildCard,
|
||||
teamId,
|
||||
slot,
|
||||
@ -56,6 +56,12 @@ class AccountServer(
|
||||
)
|
||||
)
|
||||
|
||||
// When the player has selected a character, we send him the list of ships to choose
|
||||
// from.
|
||||
if (message.charSelected) {
|
||||
send(BbMessage.ShipList(ships.map { it.uiName }))
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -67,7 +73,7 @@ class AccountServer(
|
||||
}
|
||||
|
||||
is BbMessage.CharSelect -> {
|
||||
if (message.select) {
|
||||
if (message.selected) {
|
||||
// Player has chosen a character.
|
||||
// TODO: Verify slot.
|
||||
if (slot in 0..3) {
|
||||
@ -75,7 +81,7 @@ class AccountServer(
|
||||
charSelected = true
|
||||
send(
|
||||
BbMessage.AuthData(
|
||||
BbAuthStatus.Success,
|
||||
AuthStatus.Success,
|
||||
guildCard,
|
||||
teamId,
|
||||
slot,
|
||||
@ -83,11 +89,11 @@ class AccountServer(
|
||||
)
|
||||
)
|
||||
send(
|
||||
BbMessage.CharSelectAck(slot, BbCharSelectStatus.Select)
|
||||
BbMessage.CharSelectAck(slot, CharSelectStatus.Select)
|
||||
)
|
||||
} else {
|
||||
send(
|
||||
BbMessage.CharSelectAck(slot, BbCharSelectStatus.Nonexistent)
|
||||
BbMessage.CharSelectAck(slot, CharSelectStatus.Nonexistent)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -188,6 +194,19 @@ class AccountServer(
|
||||
true
|
||||
}
|
||||
|
||||
is BbMessage.MenuSelect -> {
|
||||
if (message.menuType == MenuType.Ship) {
|
||||
ships.getOrNull(message.itemNo - 1)?.let { ship ->
|
||||
send(BbMessage.Redirect(ship.bindPair.address.address, ship.bindPair.port))
|
||||
}
|
||||
|
||||
// Disconnect.
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
is BbMessage.Disconnect -> false
|
||||
|
||||
else -> unexpectedMessage(message)
|
||||
|
@ -2,17 +2,16 @@ package world.phantasmal.psoserv.servers
|
||||
|
||||
import world.phantasmal.psoserv.encryption.BbCipher
|
||||
import world.phantasmal.psoserv.encryption.Cipher
|
||||
import world.phantasmal.psoserv.messages.BbAuthStatus
|
||||
import world.phantasmal.psoserv.messages.AuthStatus
|
||||
import world.phantasmal.psoserv.messages.BbMessage
|
||||
import world.phantasmal.psoserv.messages.BbMessageDescriptor
|
||||
import java.net.Inet4Address
|
||||
|
||||
class AuthServer(
|
||||
name: String,
|
||||
bindPair: Inet4Pair,
|
||||
private val accountServerAddress: Inet4Address,
|
||||
private val accountServerPort: Int,
|
||||
) : GameServer<BbMessage>(name, bindPair) {
|
||||
) : GameServer<BbMessage>("auth", bindPair) {
|
||||
|
||||
override val messageDescriptor = BbMessageDescriptor
|
||||
|
||||
@ -39,7 +38,7 @@ class AuthServer(
|
||||
// TODO: Actual authentication.
|
||||
send(
|
||||
BbMessage.AuthData(
|
||||
BbAuthStatus.Success,
|
||||
AuthStatus.Success,
|
||||
message.guildCard,
|
||||
message.teamId,
|
||||
slot = 0,
|
||||
|
@ -6,10 +6,9 @@ import world.phantasmal.psoserv.messages.PcMessage
|
||||
import world.phantasmal.psoserv.messages.PcMessageDescriptor
|
||||
|
||||
class PatchServer(
|
||||
name: String,
|
||||
bindPair: Inet4Pair,
|
||||
private val welcomeMessage: String,
|
||||
) : GameServer<PcMessage>(name, bindPair) {
|
||||
) : GameServer<PcMessage>("patch", bindPair) {
|
||||
|
||||
override val messageDescriptor = PcMessageDescriptor
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
package world.phantasmal.psoserv.servers
|
||||
|
||||
class ShipInfo(
|
||||
val name: String,
|
||||
val uiName: String,
|
||||
val bindPair: Inet4Pair,
|
||||
)
|
@ -0,0 +1,61 @@
|
||||
package world.phantasmal.psoserv.servers
|
||||
|
||||
import world.phantasmal.psoserv.encryption.BbCipher
|
||||
import world.phantasmal.psoserv.encryption.Cipher
|
||||
import world.phantasmal.psoserv.messages.AuthStatus
|
||||
import world.phantasmal.psoserv.messages.BbMessage
|
||||
import world.phantasmal.psoserv.messages.BbMessageDescriptor
|
||||
|
||||
class ShipServer(
|
||||
name: String,
|
||||
bindPair: Inet4Pair,
|
||||
private val uiName: String,
|
||||
) : GameServer<BbMessage>(name, bindPair) {
|
||||
|
||||
override val messageDescriptor = BbMessageDescriptor
|
||||
|
||||
override fun createCipher() = BbCipher()
|
||||
|
||||
override fun createClientReceiver(
|
||||
sender: ClientSender<BbMessage>,
|
||||
serverCipher: Cipher,
|
||||
clientCipher: Cipher,
|
||||
): ClientReceiver<BbMessage> = object : ClientReceiver<BbMessage> {
|
||||
init {
|
||||
sender.send(
|
||||
BbMessage.InitEncryption(
|
||||
"Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.",
|
||||
serverCipher.key,
|
||||
clientCipher.key,
|
||||
),
|
||||
encrypt = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun process(message: BbMessage): Boolean = when (message) {
|
||||
is BbMessage.Authenticate -> {
|
||||
// TODO: Actual authentication.
|
||||
send(
|
||||
BbMessage.AuthData(
|
||||
AuthStatus.Success,
|
||||
message.guildCard,
|
||||
message.teamId,
|
||||
message.charSlot,
|
||||
message.charSelected,
|
||||
)
|
||||
)
|
||||
send(
|
||||
BbMessage.BlockList(uiName, listOf("BLOCK01"))
|
||||
)
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
else -> unexpectedMessage(message)
|
||||
}
|
||||
|
||||
private fun send(message: BbMessage) {
|
||||
sender.send(message)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user