mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 06:28:28 +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_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> {
|
||||
override val headerSize: Int = BB_HEADER_SIZE
|
||||
|
||||
@ -97,11 +34,12 @@ object BbMessageDescriptor : MessageDescriptor<BbMessage> {
|
||||
0x03DC -> BbMessage.GetGuildCardChunk(buffer)
|
||||
0x00E0 -> BbMessage.GetAccount(buffer)
|
||||
0x00E2 -> BbMessage.Account(buffer)
|
||||
0x00E3 -> BbMessage.CharacterSelect(buffer)
|
||||
0x00E5 -> BbMessage.CharacterSelectResponse(buffer)
|
||||
0x00E6 -> BbMessage.AuthenticationResponse(buffer)
|
||||
0x00E3 -> BbMessage.CharSelect(buffer)
|
||||
0x00E4 -> BbMessage.CharSelectAck(buffer)
|
||||
0x00E5 -> BbMessage.CharData(buffer)
|
||||
0x00E6 -> BbMessage.AuthData(buffer)
|
||||
0x01E8 -> BbMessage.Checksum(buffer)
|
||||
0x02E8 -> BbMessage.ChecksumResponse(buffer)
|
||||
0x02E8 -> BbMessage.ChecksumAck(buffer)
|
||||
0x03E8 -> BbMessage.GetGuildCardHeader(buffer)
|
||||
0x01EB -> BbMessage.FileList(buffer)
|
||||
0x02EB -> BbMessage.FileChunk(buffer)
|
||||
@ -243,12 +181,30 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
}
|
||||
|
||||
// 0x00E3
|
||||
class CharacterSelect(buffer: Buffer) : BbMessage(buffer) {
|
||||
class CharSelect(buffer: Buffer) : BbMessage(buffer) {
|
||||
val slot: Int get() = uByte(0).toInt()
|
||||
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(
|
||||
buf(0x00E5, 128) {
|
||||
writeInt(char.slot)
|
||||
@ -281,24 +237,33 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
)
|
||||
}
|
||||
|
||||
class AuthenticationResponse(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(status: BbAuthenticationStatus, guildCard: Int, teamId: Int) : this(
|
||||
class AuthData(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(
|
||||
status: BbAuthStatus,
|
||||
guildCard: Int,
|
||||
teamId: Int,
|
||||
slot: Int,
|
||||
selected: Boolean,
|
||||
) : this(
|
||||
buf(0x00E6, 60) {
|
||||
writeInt(
|
||||
when (status) {
|
||||
BbAuthenticationStatus.Success -> 0
|
||||
BbAuthenticationStatus.Error -> 1
|
||||
BbAuthenticationStatus.UnknownUser -> 8
|
||||
BbAuthStatus.Success -> 0
|
||||
BbAuthStatus.Error -> 1
|
||||
BbAuthStatus.Nonexistent -> 8
|
||||
}
|
||||
)
|
||||
writeInt(0x10000)
|
||||
writeInt(guildCard)
|
||||
writeInt(teamId)
|
||||
writeInt(
|
||||
if (status == BbAuthenticationStatus.Success) (0xDEADBEEF).toInt() else 0
|
||||
if (status == BbAuthStatus.Success) (0xDEADBEEF).toInt() else 0
|
||||
)
|
||||
// 36 Bytes of unknown data.
|
||||
repeat(9) { writeInt(0) }
|
||||
writeByte(slot.toByte())
|
||||
writeByte(if (selected) 1 else 0)
|
||||
// 34 Bytes of unknown data.
|
||||
writeShort(0)
|
||||
repeat(8) { writeInt(0) }
|
||||
writeInt(0x102)
|
||||
}
|
||||
)
|
||||
@ -315,7 +280,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
|
||||
val checksum: Int get() = int(0)
|
||||
}
|
||||
|
||||
class ChecksumResponse(buffer: Buffer) : BbMessage(buffer) {
|
||||
class ChecksumAck(buffer: Buffer) : BbMessage(buffer) {
|
||||
constructor(success: Boolean) : this(
|
||||
buf(0x02E8, 4) {
|
||||
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)
|
||||
|
||||
companion object {
|
||||
protected fun buf(
|
||||
private fun buf(
|
||||
code: Int,
|
||||
bodySize: 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)
|
||||
|
||||
companion object {
|
||||
protected fun buf(
|
||||
private fun buf(
|
||||
code: Int,
|
||||
bodySize: Int = 0,
|
||||
writeBody: WritableCursor.() -> Unit = {},
|
||||
|
@ -5,10 +5,7 @@ import world.phantasmal.core.math.clamp
|
||||
import world.phantasmal.psolib.Endianness
|
||||
import world.phantasmal.psolib.buffer.Buffer
|
||||
import world.phantasmal.psolib.cursor.cursor
|
||||
import world.phantasmal.psoserv.messages.BbAuthenticationStatus
|
||||
import world.phantasmal.psoserv.messages.BbMessage
|
||||
import world.phantasmal.psoserv.messages.FileListEntry
|
||||
import world.phantasmal.psoserv.messages.PsoCharacter
|
||||
import world.phantasmal.psoserv.messages.*
|
||||
import world.phantasmal.psoserv.servers.FinalServerState
|
||||
import world.phantasmal.psoserv.servers.ServerState
|
||||
import world.phantasmal.psoserv.servers.ServerStateContext
|
||||
@ -18,6 +15,10 @@ import world.phantasmal.psoserv.utils.crc32Checksum
|
||||
class AccountContext(
|
||||
logger: KLogger,
|
||||
socketSender: SocketSender<BbMessage>,
|
||||
var guildCard: Int = -1,
|
||||
var teamId: Int = -1,
|
||||
var slot: Int = 0,
|
||||
var charSelected: Boolean = false,
|
||||
) : ServerStateContext<BbMessage>(logger, socketSender)
|
||||
|
||||
sealed class AccountState(ctx: AccountContext) :
|
||||
@ -27,80 +28,105 @@ sealed class AccountState(ctx: AccountContext) :
|
||||
override fun process(message: BbMessage): AccountState =
|
||||
if (message is BbMessage.Authenticate) {
|
||||
// TODO: Actual authentication.
|
||||
ctx.guildCard = message.guildCard
|
||||
ctx.teamId = message.teamId
|
||||
ctx.send(
|
||||
BbMessage.AuthenticationResponse(
|
||||
BbAuthenticationStatus.Success,
|
||||
message.guildCard,
|
||||
message.teamId,
|
||||
BbMessage.AuthData(
|
||||
BbAuthStatus.Success,
|
||||
ctx.guildCard,
|
||||
ctx.teamId,
|
||||
ctx.slot,
|
||||
ctx.charSelected,
|
||||
)
|
||||
)
|
||||
|
||||
GetAccount(ctx)
|
||||
Account(ctx)
|
||||
} else {
|
||||
unexpectedMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
class GetAccount(ctx: AccountContext) : AccountState(ctx) {
|
||||
override fun process(message: BbMessage): AccountState =
|
||||
if (message is BbMessage.GetAccount) {
|
||||
// TODO: Send correct guild card number and team ID.
|
||||
ctx.send(BbMessage.Account(0, 0))
|
||||
|
||||
GetCharacters(ctx)
|
||||
} else {
|
||||
unexpectedMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
class GetCharacters(ctx: AccountContext) : AccountState(ctx) {
|
||||
class Account(ctx: AccountContext) : AccountState(ctx) {
|
||||
override fun process(message: BbMessage): AccountState =
|
||||
when (message) {
|
||||
is BbMessage.CharacterSelect -> {
|
||||
// TODO: Look up character data.
|
||||
ctx.send(
|
||||
BbMessage.CharacterSelectResponse(
|
||||
PsoCharacter(
|
||||
slot = message.slot,
|
||||
exp = 0,
|
||||
level = 0,
|
||||
guildCardString = "",
|
||||
nameColor = 0,
|
||||
model = 0,
|
||||
nameColorChecksum = 0,
|
||||
sectionId = message.slot,
|
||||
characterClass = message.slot,
|
||||
costume = 0,
|
||||
skin = 0,
|
||||
face = 0,
|
||||
head = 0,
|
||||
hair = 0,
|
||||
hairRed = 0,
|
||||
hairGreen = 0,
|
||||
hairBlue = 0,
|
||||
propX = 0.5,
|
||||
propY = 0.5,
|
||||
name = "Phantasmal ${message.slot}",
|
||||
playTime = 0,
|
||||
is BbMessage.GetAccount -> {
|
||||
// TODO: Send correct guild card number and team ID.
|
||||
ctx.send(BbMessage.Account(0, 0))
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
is BbMessage.CharSelect -> {
|
||||
if (message.select) {
|
||||
// TODO: Verify slot.
|
||||
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.
|
||||
ctx.send(
|
||||
BbMessage.CharData(
|
||||
PsoCharacter(
|
||||
slot = message.slot,
|
||||
exp = 0,
|
||||
level = 0,
|
||||
guildCardString = "",
|
||||
nameColor = 0,
|
||||
model = 0,
|
||||
nameColorChecksum = 0,
|
||||
sectionId = message.slot,
|
||||
characterClass = message.slot,
|
||||
costume = 0,
|
||||
skin = 0,
|
||||
face = 0,
|
||||
head = 0,
|
||||
hair = 0,
|
||||
hairRed = 0,
|
||||
hairGreen = 0,
|
||||
hairBlue = 0,
|
||||
propX = 0.5,
|
||||
propY = 0.5,
|
||||
name = "Phantasmal ${message.slot}",
|
||||
playTime = 0,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
is BbMessage.Checksum -> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
class GetGuildCardData(ctx: AccountContext) : AccountState(ctx) {
|
||||
class GuildCardData(ctx: AccountContext) : AccountState(ctx) {
|
||||
private val guildCardBuffer = Buffer.withSize(54672)
|
||||
|
||||
override fun process(message: BbMessage): AccountState =
|
||||
@ -134,7 +160,7 @@ sealed class AccountState(ctx: AccountContext) :
|
||||
|
||||
this
|
||||
} 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
|
||||
|
||||
override fun process(message: BbMessage): AccountState =
|
||||
|
@ -1,7 +1,7 @@
|
||||
package world.phantasmal.psoserv.servers.auth
|
||||
|
||||
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.servers.FinalServerState
|
||||
import world.phantasmal.psoserv.servers.ServerState
|
||||
@ -23,10 +23,12 @@ sealed class AuthState(ctx: AuthContext) :
|
||||
if (message is BbMessage.Authenticate) {
|
||||
// TODO: Actual authentication.
|
||||
ctx.send(
|
||||
BbMessage.AuthenticationResponse(
|
||||
BbAuthenticationStatus.Success,
|
||||
BbMessage.AuthData(
|
||||
BbAuthStatus.Success,
|
||||
message.guildCard,
|
||||
message.teamId,
|
||||
slot = 0,
|
||||
selected = false,
|
||||
)
|
||||
)
|
||||
ctx.send(
|
||||
|
Loading…
Reference in New Issue
Block a user