psoserv now serves all account data.

This commit is contained in:
Daan Vanden Bosch 2021-08-01 16:59:34 +02:00
parent 73e53177a8
commit fccf2255d3
12 changed files with 108 additions and 38 deletions

View File

@ -42,7 +42,7 @@ class PsoCharacter(
init { init {
require(slot in 0..3) require(slot in 0..3)
require(exp >= 0) require(exp >= 0)
require(level in 1..200) require(level in 0..199)
require(guildCardString.length <= 16) require(guildCardString.length <= 16)
require(name.length <= 16) require(name.length <= 16)
require(playTime >= 0) require(playTime >= 0)
@ -62,6 +62,19 @@ class GuildCard(
val entries: List<GuildCardEntry> 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
@ -315,7 +328,16 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
} }
class FileList(buffer: Buffer) : BbMessage(buffer) { class FileList(buffer: Buffer) : BbMessage(buffer) {
constructor() : this(buf(0x01EB)) constructor(entries: List<FileListEntry>) : this(
buf(0x01EB, entries.size * 76, flags = entries.size) {
for (entry in entries) {
writeInt(entry.size)
writeInt(entry.checksum)
writeInt(entry.offset)
writeStringAscii(entry.filename, byteLength = 64)
}
}
)
} }
class FileChunk(buffer: Buffer) : BbMessage(buffer) { class FileChunk(buffer: Buffer) : BbMessage(buffer) {
@ -341,6 +363,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
protected fun buf( protected fun buf(
code: Int, code: Int,
bodySize: Int = 0, bodySize: Int = 0,
flags: Int = 0,
writeBody: WritableCursor.() -> Unit = {}, writeBody: WritableCursor.() -> Unit = {},
): Buffer { ): Buffer {
val size = BB_HEADER_SIZE + bodySize val size = BB_HEADER_SIZE + bodySize
@ -350,7 +373,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
// Write header. // Write header.
.writeShort(size.toShort()) .writeShort(size.toShort())
.writeShort(code.toShort()) .writeShort(code.toShort())
.writeInt(0) // Flags. .writeInt(flags)
cursor.writeBody() cursor.writeBody()

View File

@ -2,16 +2,18 @@ package world.phantasmal.psoserv.servers.account
import mu.KLogger import mu.KLogger
import world.phantasmal.core.math.clamp import world.phantasmal.core.math.clamp
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.BbAuthenticationStatus
import world.phantasmal.psoserv.messages.BbMessage import world.phantasmal.psoserv.messages.BbMessage
import world.phantasmal.psoserv.messages.FileListEntry
import world.phantasmal.psoserv.messages.PsoCharacter 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
import world.phantasmal.psoserv.servers.SocketSender import world.phantasmal.psoserv.servers.SocketSender
import kotlin.math.min import world.phantasmal.psoserv.utils.crc32Checksum
class AccountContext( class AccountContext(
logger: KLogger, logger: KLogger,
@ -61,13 +63,13 @@ sealed class AccountState(ctx: AccountContext) :
PsoCharacter( PsoCharacter(
slot = message.slot, slot = message.slot,
exp = 0, exp = 0,
level = 1, level = 0,
guildCardString = "", guildCardString = "",
nameColor = 0, nameColor = 0,
model = 0, model = 0,
nameColorChecksum = 0, nameColorChecksum = 0,
sectionId = 0, sectionId = message.slot,
characterClass = 0, characterClass = message.slot,
costume = 0, costume = 0,
skin = 0, skin = 0,
face = 0, face = 0,
@ -76,8 +78,8 @@ sealed class AccountState(ctx: AccountContext) :
hairRed = 0, hairRed = 0,
hairGreen = 0, hairGreen = 0,
hairBlue = 0, hairBlue = 0,
propX = 1.0, propX = 0.5,
propY = 1.0, propY = 0.5,
name = "Phantasmal ${message.slot}", name = "Phantasmal ${message.slot}",
playTime = 0, playTime = 0,
) )
@ -107,7 +109,7 @@ sealed class AccountState(ctx: AccountContext) :
ctx.send( ctx.send(
BbMessage.GuildCardHeader( BbMessage.GuildCardHeader(
guildCardBuffer.size, guildCardBuffer.size,
crc32(guildCardBuffer), crc32Checksum(guildCardBuffer),
) )
) )
@ -116,9 +118,12 @@ sealed class AccountState(ctx: AccountContext) :
is BbMessage.GetGuildCardChunk -> { is BbMessage.GetGuildCardChunk -> {
if (message.cont) { if (message.cont) {
val offset = val offset = clamp(
clamp(message.chunkNo * MAX_CHUNK_SIZE, 0, guildCardBuffer.size) message.chunkNo * MAX_CHUNK_SIZE,
val size = min(guildCardBuffer.size - offset, MAX_CHUNK_SIZE) min = 0,
max = guildCardBuffer.size,
)
val size = (guildCardBuffer.size - offset).coerceAtMost(MAX_CHUNK_SIZE)
ctx.send( ctx.send(
BbMessage.GuildCardChunk( BbMessage.GuildCardChunk(
@ -136,60 +141,79 @@ sealed class AccountState(ctx: AccountContext) :
else -> unexpectedMessage(message) else -> unexpectedMessage(message)
} }
private fun crc32(data: Buffer): Int {
val cursor = data.cursor()
var cs = 0xFFFFFFFFu
while (cursor.hasBytesLeft()) {
cs = cs xor cursor.uByte().toUInt()
for (i in 0..7) {
cs = if ((cs and 1u) == 0u) {
cs shr 1
} else {
(cs shr 1) xor 0xEDB88320u
}
}
}
return (cs xor 0xFFFFFFFFu).toInt()
}
companion object { companion object {
private const val MAX_CHUNK_SIZE: Int = 0x6800 private const val MAX_CHUNK_SIZE: Int = 0x6800
} }
} }
class GetFiles(ctx: AccountContext) : AccountState(ctx) { class GetFiles(ctx: AccountContext) : AccountState(ctx) {
private val fileBuffer = Buffer.withSize(0)
private var fileChunkNo = 0 private var fileChunkNo = 0
override fun process(message: BbMessage): AccountState = override fun process(message: BbMessage): AccountState =
when (message) { when (message) {
is BbMessage.GetFileList -> { is BbMessage.GetFileList -> {
ctx.send(BbMessage.FileList()) ctx.send(BbMessage.FileList(FILE_LIST))
this this
} }
is BbMessage.GetFileChunk -> { is BbMessage.GetFileChunk -> {
val offset = min(fileChunkNo * MAX_CHUNK_SIZE, fileBuffer.size) val offset = (fileChunkNo * MAX_CHUNK_SIZE).coerceAtMost(FILE_BUFFER.size)
val size = min(fileBuffer.size - offset, MAX_CHUNK_SIZE) val size = (FILE_BUFFER.size - offset).coerceAtMost(MAX_CHUNK_SIZE)
ctx.send(BbMessage.FileChunk(fileChunkNo, fileBuffer.cursor(offset, size))) ctx.send(BbMessage.FileChunk(fileChunkNo, FILE_BUFFER.cursor(offset, size)))
if (offset + size < fileBuffer.size) { if (offset + size < FILE_BUFFER.size) {
fileChunkNo++ fileChunkNo++
} }
this this
} }
is BbMessage.Disconnect -> Final(ctx)
else -> unexpectedMessage(message) else -> unexpectedMessage(message)
} }
companion object { companion object {
private const val MAX_CHUNK_SIZE: Int = 0x6800 private const val MAX_CHUNK_SIZE: Int = 0x6800
private val FILE_LIST: List<FileListEntry>
private val FILE_BUFFER: Buffer
init {
val filenames = listOf(
"BattleParamEntry.dat",
"BattleParamEntry_ep4.dat",
"BattleParamEntry_ep4_on.dat",
"BattleParamEntry_lab.dat",
"BattleParamEntry_lab_on.dat",
"BattleParamEntry_on.dat",
"ItemMagEdit.prs",
"ItemPMT.prs",
"PlyLevelTbl.prs",
)
val fileBuffers = mutableListOf<Buffer>()
val fileList = mutableListOf<FileListEntry>()
var offset = 0
for (filename in filenames) {
val data = Buffer.fromResource("/world/phantasmal/psoserv/$filename")
fileList.add(FileListEntry(data.size, crc32Checksum(data), offset, filename))
fileBuffers.add(data)
offset += data.size
}
FILE_LIST = fileList
FILE_BUFFER = Buffer.withSize(offset, Endianness.Little)
offset = 0
for (data in fileBuffers) {
data.copyInto(FILE_BUFFER, destinationOffset = offset)
offset += data.size
}
}
} }
} }

View File

@ -0,0 +1,23 @@
package world.phantasmal.psoserv.utils
import world.phantasmal.psolib.buffer.Buffer
import world.phantasmal.psolib.cursor.cursor
fun crc32Checksum(data: Buffer): Int {
val cursor = data.cursor()
var cs = 0xFFFFFFFFu
while (cursor.hasBytesLeft()) {
cs = cs xor cursor.uByte().toUInt()
for (i in 0..7) {
cs = if ((cs and 1u) == 0u) {
cs shr 1
} else {
(cs shr 1) xor 0xEDB88320u
}
}
}
return (cs xor 0xFFFFFFFFu).toInt()
}