mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
Character selection on psoserv now works.
This commit is contained in:
parent
fccf2255d3
commit
f6e0ed06a9
@ -12,69 +12,6 @@ const val BB_HEADER_SIZE: Int = 8
|
|||||||
const val BB_MSG_SIZE_POS: Int = 0
|
const val BB_MSG_SIZE_POS: Int = 0
|
||||||
const val BB_MSG_CODE_POS: Int = 2
|
const val BB_MSG_CODE_POS: Int = 2
|
||||||
|
|
||||||
enum class BbAuthenticationStatus {
|
|
||||||
Success, Error, UnknownUser
|
|
||||||
}
|
|
||||||
|
|
||||||
class PsoCharacter(
|
|
||||||
val slot: Int,
|
|
||||||
val exp: Int,
|
|
||||||
val level: Int,
|
|
||||||
val guildCardString: String,
|
|
||||||
val nameColor: Int,
|
|
||||||
val model: Int,
|
|
||||||
val nameColorChecksum: Int,
|
|
||||||
val sectionId: Int,
|
|
||||||
val characterClass: Int,
|
|
||||||
val costume: Int,
|
|
||||||
val skin: Int,
|
|
||||||
val face: Int,
|
|
||||||
val head: Int,
|
|
||||||
val hair: Int,
|
|
||||||
val hairRed: Int,
|
|
||||||
val hairGreen: Int,
|
|
||||||
val hairBlue: Int,
|
|
||||||
val propX: Double,
|
|
||||||
val propY: Double,
|
|
||||||
val name: String,
|
|
||||||
val playTime: Int,
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
require(slot in 0..3)
|
|
||||||
require(exp >= 0)
|
|
||||||
require(level in 0..199)
|
|
||||||
require(guildCardString.length <= 16)
|
|
||||||
require(name.length <= 16)
|
|
||||||
require(playTime >= 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GuildCardEntry(
|
|
||||||
val playerTag: Int,
|
|
||||||
val serialNumber: Int,
|
|
||||||
val name: String,
|
|
||||||
val description: String,
|
|
||||||
val sectionId: Int,
|
|
||||||
val characterClass: Int,
|
|
||||||
)
|
|
||||||
|
|
||||||
class GuildCard(
|
|
||||||
val entries: List<GuildCardEntry>
|
|
||||||
)
|
|
||||||
|
|
||||||
class FileListEntry(
|
|
||||||
val size: Int,
|
|
||||||
val checksum: Int,
|
|
||||||
val offset: Int,
|
|
||||||
val filename: String,
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
require(size > 0)
|
|
||||||
require(offset >= 0)
|
|
||||||
require(filename.length <= 64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object BbMessageDescriptor : MessageDescriptor<BbMessage> {
|
object BbMessageDescriptor : MessageDescriptor<BbMessage> {
|
||||||
override val headerSize: Int = BB_HEADER_SIZE
|
override val headerSize: Int = BB_HEADER_SIZE
|
||||||
|
|
||||||
@ -97,11 +34,12 @@ object BbMessageDescriptor : MessageDescriptor<BbMessage> {
|
|||||||
0x03DC -> BbMessage.GetGuildCardChunk(buffer)
|
0x03DC -> BbMessage.GetGuildCardChunk(buffer)
|
||||||
0x00E0 -> BbMessage.GetAccount(buffer)
|
0x00E0 -> BbMessage.GetAccount(buffer)
|
||||||
0x00E2 -> BbMessage.Account(buffer)
|
0x00E2 -> BbMessage.Account(buffer)
|
||||||
0x00E3 -> BbMessage.CharacterSelect(buffer)
|
0x00E3 -> BbMessage.CharSelect(buffer)
|
||||||
0x00E5 -> BbMessage.CharacterSelectResponse(buffer)
|
0x00E4 -> BbMessage.CharSelectAck(buffer)
|
||||||
0x00E6 -> BbMessage.AuthenticationResponse(buffer)
|
0x00E5 -> BbMessage.CharData(buffer)
|
||||||
|
0x00E6 -> BbMessage.AuthData(buffer)
|
||||||
0x01E8 -> BbMessage.Checksum(buffer)
|
0x01E8 -> BbMessage.Checksum(buffer)
|
||||||
0x02E8 -> BbMessage.ChecksumResponse(buffer)
|
0x02E8 -> BbMessage.ChecksumAck(buffer)
|
||||||
0x03E8 -> BbMessage.GetGuildCardHeader(buffer)
|
0x03E8 -> BbMessage.GetGuildCardHeader(buffer)
|
||||||
0x01EB -> BbMessage.FileList(buffer)
|
0x01EB -> BbMessage.FileList(buffer)
|
||||||
0x02EB -> BbMessage.FileChunk(buffer)
|
0x02EB -> BbMessage.FileChunk(buffer)
|
||||||
@ -243,12 +181,30 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0x00E3
|
// 0x00E3
|
||||||
class CharacterSelect(buffer: Buffer) : BbMessage(buffer) {
|
class CharSelect(buffer: Buffer) : BbMessage(buffer) {
|
||||||
val slot: Int get() = uByte(0).toInt()
|
val slot: Int get() = uByte(0).toInt()
|
||||||
val select: Boolean get() = byte(4).toInt() != 0
|
val select: Boolean get() = byte(4).toInt() != 0
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
messageString("slot" to slot, "select" to select)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CharacterSelectResponse(buffer: Buffer) : BbMessage(buffer) {
|
class CharSelectAck(buffer: Buffer) : BbMessage(buffer) {
|
||||||
|
constructor(slot: Int, status: BbCharSelectStatus) : this(
|
||||||
|
buf(0x00E4, 8) {
|
||||||
|
writeInt(slot)
|
||||||
|
writeInt(
|
||||||
|
when (status) {
|
||||||
|
BbCharSelectStatus.Update -> 0
|
||||||
|
BbCharSelectStatus.Select -> 1
|
||||||
|
BbCharSelectStatus.Nonexistent -> 2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharData(buffer: Buffer) : BbMessage(buffer) {
|
||||||
constructor(char: PsoCharacter) : this(
|
constructor(char: PsoCharacter) : this(
|
||||||
buf(0x00E5, 128) {
|
buf(0x00E5, 128) {
|
||||||
writeInt(char.slot)
|
writeInt(char.slot)
|
||||||
@ -281,24 +237,33 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthenticationResponse(buffer: Buffer) : BbMessage(buffer) {
|
class AuthData(buffer: Buffer) : BbMessage(buffer) {
|
||||||
constructor(status: BbAuthenticationStatus, guildCard: Int, teamId: Int) : this(
|
constructor(
|
||||||
|
status: BbAuthStatus,
|
||||||
|
guildCard: Int,
|
||||||
|
teamId: Int,
|
||||||
|
slot: Int,
|
||||||
|
selected: Boolean,
|
||||||
|
) : this(
|
||||||
buf(0x00E6, 60) {
|
buf(0x00E6, 60) {
|
||||||
writeInt(
|
writeInt(
|
||||||
when (status) {
|
when (status) {
|
||||||
BbAuthenticationStatus.Success -> 0
|
BbAuthStatus.Success -> 0
|
||||||
BbAuthenticationStatus.Error -> 1
|
BbAuthStatus.Error -> 1
|
||||||
BbAuthenticationStatus.UnknownUser -> 8
|
BbAuthStatus.Nonexistent -> 8
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
writeInt(0x10000)
|
writeInt(0x10000)
|
||||||
writeInt(guildCard)
|
writeInt(guildCard)
|
||||||
writeInt(teamId)
|
writeInt(teamId)
|
||||||
writeInt(
|
writeInt(
|
||||||
if (status == BbAuthenticationStatus.Success) (0xDEADBEEF).toInt() else 0
|
if (status == BbAuthStatus.Success) (0xDEADBEEF).toInt() else 0
|
||||||
)
|
)
|
||||||
// 36 Bytes of unknown data.
|
writeByte(slot.toByte())
|
||||||
repeat(9) { writeInt(0) }
|
writeByte(if (selected) 1 else 0)
|
||||||
|
// 34 Bytes of unknown data.
|
||||||
|
writeShort(0)
|
||||||
|
repeat(8) { writeInt(0) }
|
||||||
writeInt(0x102)
|
writeInt(0x102)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -315,7 +280,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
|||||||
val checksum: Int get() = int(0)
|
val checksum: Int get() = int(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChecksumResponse(buffer: Buffer) : BbMessage(buffer) {
|
class ChecksumAck(buffer: Buffer) : BbMessage(buffer) {
|
||||||
constructor(success: Boolean) : this(
|
constructor(success: Boolean) : this(
|
||||||
buf(0x02E8, 4) {
|
buf(0x02E8, 4) {
|
||||||
writeInt(if (success) 1 else 0)
|
writeInt(if (success) 1 else 0)
|
||||||
@ -360,7 +325,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
|||||||
class Unknown(buffer: Buffer) : BbMessage(buffer)
|
class Unknown(buffer: Buffer) : BbMessage(buffer)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
protected fun buf(
|
private fun buf(
|
||||||
code: Int,
|
code: Int,
|
||||||
bodySize: Int = 0,
|
bodySize: Int = 0,
|
||||||
flags: Int = 0,
|
flags: Int = 0,
|
||||||
@ -385,3 +350,55 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class BbAuthStatus {
|
||||||
|
Success, Error, Nonexistent
|
||||||
|
}
|
||||||
|
|
||||||
|
class PsoCharacter(
|
||||||
|
val slot: Int,
|
||||||
|
val exp: Int,
|
||||||
|
val level: Int,
|
||||||
|
val guildCardString: String,
|
||||||
|
val nameColor: Int,
|
||||||
|
val model: Int,
|
||||||
|
val nameColorChecksum: Int,
|
||||||
|
val sectionId: Int,
|
||||||
|
val characterClass: Int,
|
||||||
|
val costume: Int,
|
||||||
|
val skin: Int,
|
||||||
|
val face: Int,
|
||||||
|
val head: Int,
|
||||||
|
val hair: Int,
|
||||||
|
val hairRed: Int,
|
||||||
|
val hairGreen: Int,
|
||||||
|
val hairBlue: Int,
|
||||||
|
val propX: Double,
|
||||||
|
val propY: Double,
|
||||||
|
val name: String,
|
||||||
|
val playTime: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
class GuildCardEntry(
|
||||||
|
val playerTag: Int,
|
||||||
|
val serialNumber: Int,
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val sectionId: Int,
|
||||||
|
val characterClass: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
class GuildCard(
|
||||||
|
val entries: List<GuildCardEntry>
|
||||||
|
)
|
||||||
|
|
||||||
|
class FileListEntry(
|
||||||
|
val size: Int,
|
||||||
|
val checksum: Int,
|
||||||
|
val offset: Int,
|
||||||
|
val filename: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class BbCharSelectStatus {
|
||||||
|
Update, Select, Nonexistent
|
||||||
|
}
|
||||||
|
@ -130,7 +130,7 @@ sealed class PcMessage(override val buffer: Buffer) : AbstractMessage(PC_HEADER_
|
|||||||
class Unknown(buffer: Buffer) : PcMessage(buffer)
|
class Unknown(buffer: Buffer) : PcMessage(buffer)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
protected fun buf(
|
private fun buf(
|
||||||
code: Int,
|
code: Int,
|
||||||
bodySize: Int = 0,
|
bodySize: Int = 0,
|
||||||
writeBody: WritableCursor.() -> Unit = {},
|
writeBody: WritableCursor.() -> Unit = {},
|
||||||
|
@ -5,10 +5,7 @@ import world.phantasmal.core.math.clamp
|
|||||||
import world.phantasmal.psolib.Endianness
|
import world.phantasmal.psolib.Endianness
|
||||||
import world.phantasmal.psolib.buffer.Buffer
|
import world.phantasmal.psolib.buffer.Buffer
|
||||||
import world.phantasmal.psolib.cursor.cursor
|
import world.phantasmal.psolib.cursor.cursor
|
||||||
import world.phantasmal.psoserv.messages.BbAuthenticationStatus
|
import world.phantasmal.psoserv.messages.*
|
||||||
import world.phantasmal.psoserv.messages.BbMessage
|
|
||||||
import world.phantasmal.psoserv.messages.FileListEntry
|
|
||||||
import world.phantasmal.psoserv.messages.PsoCharacter
|
|
||||||
import world.phantasmal.psoserv.servers.FinalServerState
|
import world.phantasmal.psoserv.servers.FinalServerState
|
||||||
import world.phantasmal.psoserv.servers.ServerState
|
import world.phantasmal.psoserv.servers.ServerState
|
||||||
import world.phantasmal.psoserv.servers.ServerStateContext
|
import world.phantasmal.psoserv.servers.ServerStateContext
|
||||||
@ -18,6 +15,10 @@ import world.phantasmal.psoserv.utils.crc32Checksum
|
|||||||
class AccountContext(
|
class AccountContext(
|
||||||
logger: KLogger,
|
logger: KLogger,
|
||||||
socketSender: SocketSender<BbMessage>,
|
socketSender: SocketSender<BbMessage>,
|
||||||
|
var guildCard: Int = -1,
|
||||||
|
var teamId: Int = -1,
|
||||||
|
var slot: Int = 0,
|
||||||
|
var charSelected: Boolean = false,
|
||||||
) : ServerStateContext<BbMessage>(logger, socketSender)
|
) : ServerStateContext<BbMessage>(logger, socketSender)
|
||||||
|
|
||||||
sealed class AccountState(ctx: AccountContext) :
|
sealed class AccountState(ctx: AccountContext) :
|
||||||
@ -27,39 +28,61 @@ sealed class AccountState(ctx: AccountContext) :
|
|||||||
override fun process(message: BbMessage): AccountState =
|
override fun process(message: BbMessage): AccountState =
|
||||||
if (message is BbMessage.Authenticate) {
|
if (message is BbMessage.Authenticate) {
|
||||||
// TODO: Actual authentication.
|
// TODO: Actual authentication.
|
||||||
|
ctx.guildCard = message.guildCard
|
||||||
|
ctx.teamId = message.teamId
|
||||||
ctx.send(
|
ctx.send(
|
||||||
BbMessage.AuthenticationResponse(
|
BbMessage.AuthData(
|
||||||
BbAuthenticationStatus.Success,
|
BbAuthStatus.Success,
|
||||||
message.guildCard,
|
ctx.guildCard,
|
||||||
message.teamId,
|
ctx.teamId,
|
||||||
|
ctx.slot,
|
||||||
|
ctx.charSelected,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
GetAccount(ctx)
|
Account(ctx)
|
||||||
} else {
|
} else {
|
||||||
unexpectedMessage(message)
|
unexpectedMessage(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetAccount(ctx: AccountContext) : AccountState(ctx) {
|
class Account(ctx: AccountContext) : AccountState(ctx) {
|
||||||
override fun process(message: BbMessage): AccountState =
|
override fun process(message: BbMessage): AccountState =
|
||||||
if (message is BbMessage.GetAccount) {
|
when (message) {
|
||||||
|
is BbMessage.GetAccount -> {
|
||||||
// TODO: Send correct guild card number and team ID.
|
// TODO: Send correct guild card number and team ID.
|
||||||
ctx.send(BbMessage.Account(0, 0))
|
ctx.send(BbMessage.Account(0, 0))
|
||||||
|
|
||||||
GetCharacters(ctx)
|
this
|
||||||
} else {
|
|
||||||
unexpectedMessage(message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetCharacters(ctx: AccountContext) : AccountState(ctx) {
|
is BbMessage.CharSelect -> {
|
||||||
override fun process(message: BbMessage): AccountState =
|
if (message.select) {
|
||||||
when (message) {
|
// TODO: Verify slot.
|
||||||
is BbMessage.CharacterSelect -> {
|
if (ctx.slot in 0..3) {
|
||||||
|
ctx.slot = message.slot
|
||||||
|
ctx.charSelected = true
|
||||||
|
ctx.send(
|
||||||
|
BbMessage.AuthData(
|
||||||
|
BbAuthStatus.Success,
|
||||||
|
ctx.guildCard,
|
||||||
|
ctx.teamId,
|
||||||
|
ctx.slot,
|
||||||
|
ctx.charSelected,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ctx.send(
|
||||||
|
BbMessage.CharSelectAck(ctx.slot, BbCharSelectStatus.Select)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ctx.send(
|
||||||
|
BbMessage.CharSelectAck(ctx.slot, BbCharSelectStatus.Nonexistent)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// TODO: Look up character data.
|
// TODO: Look up character data.
|
||||||
ctx.send(
|
ctx.send(
|
||||||
BbMessage.CharacterSelectResponse(
|
BbMessage.CharData(
|
||||||
PsoCharacter(
|
PsoCharacter(
|
||||||
slot = message.slot,
|
slot = message.slot,
|
||||||
exp = 0,
|
exp = 0,
|
||||||
@ -85,22 +108,25 @@ sealed class AccountState(ctx: AccountContext) :
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
is BbMessage.Checksum -> {
|
is BbMessage.Checksum -> {
|
||||||
// TODO: Checksum checking.
|
// TODO: Checksum checking.
|
||||||
ctx.send(BbMessage.ChecksumResponse(true))
|
ctx.send(BbMessage.ChecksumAck(true))
|
||||||
|
|
||||||
GetGuildCardData(ctx)
|
GuildCardData(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is BbMessage.Disconnect -> Final(ctx)
|
||||||
|
|
||||||
else -> unexpectedMessage(message)
|
else -> unexpectedMessage(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetGuildCardData(ctx: AccountContext) : AccountState(ctx) {
|
class GuildCardData(ctx: AccountContext) : AccountState(ctx) {
|
||||||
private val guildCardBuffer = Buffer.withSize(54672)
|
private val guildCardBuffer = Buffer.withSize(54672)
|
||||||
|
|
||||||
override fun process(message: BbMessage): AccountState =
|
override fun process(message: BbMessage): AccountState =
|
||||||
@ -134,7 +160,7 @@ sealed class AccountState(ctx: AccountContext) :
|
|||||||
|
|
||||||
this
|
this
|
||||||
} else {
|
} else {
|
||||||
GetFiles(ctx)
|
DownloadFiles(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +172,7 @@ sealed class AccountState(ctx: AccountContext) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetFiles(ctx: AccountContext) : AccountState(ctx) {
|
class DownloadFiles(ctx: AccountContext) : AccountState(ctx) {
|
||||||
private var fileChunkNo = 0
|
private var fileChunkNo = 0
|
||||||
|
|
||||||
override fun process(message: BbMessage): AccountState =
|
override fun process(message: BbMessage): AccountState =
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package world.phantasmal.psoserv.servers.auth
|
package world.phantasmal.psoserv.servers.auth
|
||||||
|
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
import world.phantasmal.psoserv.messages.BbAuthenticationStatus
|
import world.phantasmal.psoserv.messages.BbAuthStatus
|
||||||
import world.phantasmal.psoserv.messages.BbMessage
|
import world.phantasmal.psoserv.messages.BbMessage
|
||||||
import world.phantasmal.psoserv.servers.FinalServerState
|
import world.phantasmal.psoserv.servers.FinalServerState
|
||||||
import world.phantasmal.psoserv.servers.ServerState
|
import world.phantasmal.psoserv.servers.ServerState
|
||||||
@ -23,10 +23,12 @@ sealed class AuthState(ctx: AuthContext) :
|
|||||||
if (message is BbMessage.Authenticate) {
|
if (message is BbMessage.Authenticate) {
|
||||||
// TODO: Actual authentication.
|
// TODO: Actual authentication.
|
||||||
ctx.send(
|
ctx.send(
|
||||||
BbMessage.AuthenticationResponse(
|
BbMessage.AuthData(
|
||||||
BbAuthenticationStatus.Success,
|
BbAuthStatus.Success,
|
||||||
message.guildCard,
|
message.guildCard,
|
||||||
message.teamId,
|
message.teamId,
|
||||||
|
slot = 0,
|
||||||
|
selected = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
Loading…
Reference in New Issue
Block a user