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 {
require(slot in 0..3)
require(exp >= 0)
require(level in 1..200)
require(level in 0..199)
require(guildCardString.length <= 16)
require(name.length <= 16)
require(playTime >= 0)
@ -62,6 +62,19 @@ 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
@ -315,7 +328,16 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
}
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) {
@ -341,6 +363,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
protected fun buf(
code: Int,
bodySize: Int = 0,
flags: Int = 0,
writeBody: WritableCursor.() -> Unit = {},
): Buffer {
val size = BB_HEADER_SIZE + bodySize
@ -350,7 +373,7 @@ sealed class BbMessage(override val buffer: Buffer) : AbstractMessage(BB_HEADER_
// Write header.
.writeShort(size.toShort())
.writeShort(code.toShort())
.writeInt(0) // Flags.
.writeInt(flags)
cursor.writeBody()

View File

@ -2,16 +2,18 @@ package world.phantasmal.psoserv.servers.account
import mu.KLogger
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.servers.FinalServerState
import world.phantasmal.psoserv.servers.ServerState
import world.phantasmal.psoserv.servers.ServerStateContext
import world.phantasmal.psoserv.servers.SocketSender
import kotlin.math.min
import world.phantasmal.psoserv.utils.crc32Checksum
class AccountContext(
logger: KLogger,
@ -61,13 +63,13 @@ sealed class AccountState(ctx: AccountContext) :
PsoCharacter(
slot = message.slot,
exp = 0,
level = 1,
level = 0,
guildCardString = "",
nameColor = 0,
model = 0,
nameColorChecksum = 0,
sectionId = 0,
characterClass = 0,
sectionId = message.slot,
characterClass = message.slot,
costume = 0,
skin = 0,
face = 0,
@ -76,8 +78,8 @@ sealed class AccountState(ctx: AccountContext) :
hairRed = 0,
hairGreen = 0,
hairBlue = 0,
propX = 1.0,
propY = 1.0,
propX = 0.5,
propY = 0.5,
name = "Phantasmal ${message.slot}",
playTime = 0,
)
@ -107,7 +109,7 @@ sealed class AccountState(ctx: AccountContext) :
ctx.send(
BbMessage.GuildCardHeader(
guildCardBuffer.size,
crc32(guildCardBuffer),
crc32Checksum(guildCardBuffer),
)
)
@ -116,9 +118,12 @@ sealed class AccountState(ctx: AccountContext) :
is BbMessage.GetGuildCardChunk -> {
if (message.cont) {
val offset =
clamp(message.chunkNo * MAX_CHUNK_SIZE, 0, guildCardBuffer.size)
val size = min(guildCardBuffer.size - offset, MAX_CHUNK_SIZE)
val offset = clamp(
message.chunkNo * MAX_CHUNK_SIZE,
min = 0,
max = guildCardBuffer.size,
)
val size = (guildCardBuffer.size - offset).coerceAtMost(MAX_CHUNK_SIZE)
ctx.send(
BbMessage.GuildCardChunk(
@ -136,60 +141,79 @@ sealed class AccountState(ctx: AccountContext) :
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 {
private const val MAX_CHUNK_SIZE: Int = 0x6800
}
}
class GetFiles(ctx: AccountContext) : AccountState(ctx) {
private val fileBuffer = Buffer.withSize(0)
private var fileChunkNo = 0
override fun process(message: BbMessage): AccountState =
when (message) {
is BbMessage.GetFileList -> {
ctx.send(BbMessage.FileList())
ctx.send(BbMessage.FileList(FILE_LIST))
this
}
is BbMessage.GetFileChunk -> {
val offset = min(fileChunkNo * MAX_CHUNK_SIZE, fileBuffer.size)
val size = min(fileBuffer.size - offset, MAX_CHUNK_SIZE)
val offset = (fileChunkNo * MAX_CHUNK_SIZE).coerceAtMost(FILE_BUFFER.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++
}
this
}
is BbMessage.Disconnect -> Final(ctx)
else -> unexpectedMessage(message)
}
companion object {
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()
}