From bfcbf38878fd2c6495092ef022b1479d777229ba Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 11 May 2024 17:31:35 -0300 Subject: [PATCH] shop: basic purchase support --- .../work/fking/pangya/game/model/IffObject.kt | 36 +++++++---- .../fking/pangya/game/net/ClientPacketType.kt | 7 ++- .../ClubWorkshopRankUpPacketHandler.kt | 2 - .../handler/shop/ShopEnterUnknownHandler.kt | 13 ++++ .../handler/shop/ShopPurchaseHandler.kt | 60 +++++++++++++++++++ .../game/packet/outbound/ShopReplies.kt | 41 +++++++++++++ .../fking/pangya/game/player/Equipment.kt | 12 ++-- .../work/fking/pangya/game/player/Item.kt | 8 +-- .../pangya/game/task/ChangeClubSetStatTask.kt | 4 +- ...cterTask.kt => ShopUnlockCharacterTask.kt} | 8 ++- .../pangya/game/task/UnlockCaddieTask.kt | 4 +- 11 files changed, 161 insertions(+), 34 deletions(-) create mode 100644 game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopEnterUnknownHandler.kt create mode 100644 game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopPurchaseHandler.kt create mode 100644 game-server/src/main/kotlin/work/fking/pangya/game/packet/outbound/ShopReplies.kt rename game-server/src/main/kotlin/work/fking/pangya/game/task/{UnlockCharacterTask.kt => ShopUnlockCharacterTask.kt} (73%) diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/model/IffObject.kt b/game-server/src/main/kotlin/work/fking/pangya/game/model/IffObject.kt index a5fe95c..3bec392 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/model/IffObject.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/model/IffObject.kt @@ -2,14 +2,16 @@ package work.fking.pangya.game.model import io.netty.buffer.ByteBuf -const val IFF_TYPE_CHARACTER = 4 -const val IFF_TYPE_PART = 8 -const val IFF_TYPE_CLUBSET = 16 -const val IFF_TYPE_BALL = 20 -const val IFF_TYPE_EQUIPITEM_ITEM = 24 -const val IFF_TYPE_NOEQUIP_ITEM = 26 -const val IFF_TYPE_CADDIE = 28 -const val IFF_TYPE_CARD = 124 +enum class IffType { + CHARACTER, + PART, + CLUBSET, + BALL, + EQUIPITEM_ITEM, + NOEQUIP_ITEM, + CADDIE, + CARD +} interface IffObject { @@ -17,13 +19,21 @@ interface IffObject { val iffId: Int - fun iffTypeId(): Int { - return iffTypeFromId(iffId) - } + fun iffTypeId() = iffTypeFromId(iffId) fun encode(buffer: ByteBuf) } -fun iffTypeFromId(iffId: Int): Int { - return iffId shr 24 +fun iffTypeFromId(iffId: Int): IffType { + return when (val typeId = iffId shr 24) { + 4 -> IffType.CHARACTER + 8 -> IffType.PART + 16 -> IffType.CLUBSET + 20 -> IffType.BALL + 24 -> IffType.EQUIPITEM_ITEM + 26 -> IffType.NOEQUIP_ITEM + 28 -> IffType.CADDIE + 124 -> IffType.CARD + else -> throw IllegalArgumentException("Unknown iffTypeId $typeId") + } } \ No newline at end of file diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/net/ClientPacketType.kt b/game-server/src/main/kotlin/work/fking/pangya/game/net/ClientPacketType.kt index c37e807..af68e8d 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/net/ClientPacketType.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/net/ClientPacketType.kt @@ -1,7 +1,6 @@ package work.fking.pangya.game.net import work.fking.pangya.game.packet.handler.AchievementStatusRequestPacketHandler -import work.fking.pangya.game.packet.handler.clubworkshop.ClubSetUpgradePacketHandler import work.fking.pangya.game.packet.handler.EquipmentUpdatePacketHandler import work.fking.pangya.game.packet.handler.JoinLobbyPacketHandler import work.fking.pangya.game.packet.handler.LeaveLobbyPacketHandler @@ -13,8 +12,10 @@ import work.fking.pangya.game.packet.handler.MyRoomOpenedPacketHandler import work.fking.pangya.game.packet.handler.PapelShopPlayPacketHandler import work.fking.pangya.game.packet.handler.RareShopOpenPacketHandler import work.fking.pangya.game.packet.handler.SelectChannelPacketHandler +import work.fking.pangya.game.packet.handler.shop.ShopPurchaseHandler import work.fking.pangya.game.packet.handler.UpdateChatMacrosPacketHandler import work.fking.pangya.game.packet.handler.UserProfileRequestPacketHandler +import work.fking.pangya.game.packet.handler.clubworkshop.ClubSetUpgradePacketHandler import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopAcceptTransformPacketHandler import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopDeclineTransformPacketHandler import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopRankUpPacketHandler @@ -36,6 +37,7 @@ import work.fking.pangya.game.packet.handler.room.CreateRoomPacketHandler import work.fking.pangya.game.packet.handler.room.JoinRoomHandler import work.fking.pangya.game.packet.handler.room.LeaveRoomPacketHandler import work.fking.pangya.game.packet.handler.room.RoomSettingsUpdatePacketHandler +import work.fking.pangya.game.packet.handler.shop.ShopEnterUnknownHandler enum class ClientPacketType( private val id: Int, @@ -60,6 +62,7 @@ enum class ClientPacketType( MATCH_HOLE_START(0x1a, MatchHoleStartPacketHandler()), MATCH_PLAYER_SHOT_SYNC(0x1b, MatchPlayerShotSyncPacketHandler()), MATCH_PLAYER_SHOT_END(0x1c, MatchPlayerTurnEndPacketHandler()), + SHOP_PURCHASE(0x1d, ShopPurchaseHandler()), EQUIPMENT_UPDATE_IN_MY_ROOM(0x20, EquipmentUpdatePacketHandler()), MATCH_PLAYER_TURN_START(0x22), USER_PROFILE_REQUEST(0x2f, UserProfileRequestPacketHandler()), @@ -80,7 +83,7 @@ enum class ClientPacketType( UNKNOWN_C1(0xC1), LOCKER_INVENTORY_REQUEST(0xd3, LockerInventoryRequestPacketHandler()), MATCH_PLAYER_QUIT(0x130, MatchPlayerQuitPacketHandler()), - UNKNOWN_140(0x140), + SHOP_ENTER_UNKNOWN(0x140, ShopEnterUnknownHandler()), HOLE_REPEAT_CHANGE_WIND(0x141), ACHIEVEMENT_STATUS_REQUEST(0x157, AchievementStatusRequestPacketHandler()), PLAYER_RING_PROC(0x15d), diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/clubworkshop/ClubWorkshopRankUpPacketHandler.kt b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/clubworkshop/ClubWorkshopRankUpPacketHandler.kt index 6f320d6..04cfb92 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/clubworkshop/ClubWorkshopRankUpPacketHandler.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/clubworkshop/ClubWorkshopRankUpPacketHandler.kt @@ -2,8 +2,6 @@ package work.fking.pangya.game.packet.handler.clubworkshop import io.netty.buffer.ByteBuf import work.fking.pangya.game.GameServer -import work.fking.pangya.game.model.IFF_TYPE_CLUBSET -import work.fking.pangya.game.model.iffTypeFromId import work.fking.pangya.game.net.ClientPacketHandler import work.fking.pangya.game.packet.outbound.ClubSetReplies import work.fking.pangya.game.player.Player diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopEnterUnknownHandler.kt b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopEnterUnknownHandler.kt new file mode 100644 index 0000000..8dce154 --- /dev/null +++ b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopEnterUnknownHandler.kt @@ -0,0 +1,13 @@ +package work.fking.pangya.game.packet.handler.shop + +import io.netty.buffer.ByteBuf +import work.fking.pangya.game.GameServer +import work.fking.pangya.game.net.ClientPacketHandler +import work.fking.pangya.game.packet.outbound.ShopReplies +import work.fking.pangya.game.player.Player + +class ShopEnterUnknownHandler: ClientPacketHandler { + override fun handle(server: GameServer, player: Player, packet: ByteBuf) { + player.writeAndFlush(ShopReplies.shopUnknownReply()) + } +} \ No newline at end of file diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopPurchaseHandler.kt b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopPurchaseHandler.kt new file mode 100644 index 0000000..9d9a20c --- /dev/null +++ b/game-server/src/main/kotlin/work/fking/pangya/game/packet/handler/shop/ShopPurchaseHandler.kt @@ -0,0 +1,60 @@ +package work.fking.pangya.game.packet.handler.shop + +import io.netty.buffer.ByteBuf +import io.netty.buffer.ByteBufUtil +import org.slf4j.LoggerFactory +import work.fking.pangya.game.GameServer +import work.fking.pangya.game.model.IffType +import work.fking.pangya.game.model.iffTypeFromId +import work.fking.pangya.game.net.ClientPacketHandler +import work.fking.pangya.game.packet.outbound.PurchaseFailedReason.ALREADY_HAVE_THAT_ITEM +import work.fking.pangya.game.packet.outbound.ShopReplies +import work.fking.pangya.game.player.Player +import work.fking.pangya.game.task.ShopUnlockCharacterTask + +private val LOGGER = LoggerFactory.getLogger(ShopPurchaseHandler::class.java) + +class ShopPurchaseHandler : ClientPacketHandler { + + override fun handle(server: GameServer, player: Player, packet: ByteBuf) { + println(ByteBufUtil.prettyHexDump(packet)) + val rental = packet.readByte() + val numOfItems = packet.readUnsignedShortLE() + + repeat(numOfItems) { + val uid = packet.readIntLE() + val iffId = packet.readIntLE() + val rentalTime = packet.readUnsignedShortLE() + val unknown = packet.readUnsignedShortLE() + val quantity = packet.readIntLE() + val pangCost = packet.readIntLE() + val cookieCost = packet.readIntLE() + val couponId = packet.readIntLE() + + when (iffTypeFromId(iffId)) { + IffType.CHARACTER -> handleCharacterPurchase(server, player, iffId) + else -> handleUnknownPurchase(player, iffId) + } + } + } + + private fun handleCharacterPurchase(server: GameServer, player: Player, iffId: Int) { + if (player.characterRoster.findByIffId(iffId) != null) { + player.writeAndFlush(ShopReplies.purchaseFailed(ALREADY_HAVE_THAT_ITEM)) + return + } + + server.runTask( + ShopUnlockCharacterTask( + server.persistenceCtx, + player, + iffId + ) + ) + } + + private fun handleUnknownPurchase(player: Player, iffId: Int) { + LOGGER.warn("Player tried to purchase iffId={} type={} but we don't know how to handle it", iffId, iffTypeFromId(iffId)) + player.writeAndFlush(ShopReplies.purchaseFailed()) + } +} \ No newline at end of file diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/packet/outbound/ShopReplies.kt b/game-server/src/main/kotlin/work/fking/pangya/game/packet/outbound/ShopReplies.kt new file mode 100644 index 0000000..6747cee --- /dev/null +++ b/game-server/src/main/kotlin/work/fking/pangya/game/packet/outbound/ShopReplies.kt @@ -0,0 +1,41 @@ +package work.fking.pangya.game.packet.outbound + +import work.fking.pangya.game.packet.outbound.PurchaseFailedReason.GENERIC_ERROR +import work.fking.pangya.game.player.PlayerWallet +import work.fking.pangya.networking.protocol.OutboundPacket + +enum class PurchaseFailedReason(val code: Int) { + GENERIC_ERROR(1), + NOT_ENOUGH_PANG(2), + WRONG_ITEM_CODE(3), + ALREADY_HAVE_THAT_ITEM(4), + CHECK_ITEM_TIME_LIMIT(9), // this is maybe when trying to rent an item that is already rented + CHECK_SALE_TIMES(11) +} + +object ShopReplies { + + fun purchaseFailed(reason: PurchaseFailedReason = GENERIC_ERROR): OutboundPacket { + return OutboundPacket { buffer -> + buffer.writeShortLE(0x68) + buffer.writeIntLE(reason.code) // 1 = purchase failed, 2 = not enough pang, 3 = wrong item code, 4 = already have that item, 9 = check time limit of your item, 11 = please check sale times + } + } + + fun purchaseSuccessful(wallet: PlayerWallet): OutboundPacket { + return OutboundPacket { buffer -> + buffer.writeShortLE(0x68) + buffer.writeIntLE(0) + buffer.writeLongLE(wallet.pangBalance) + buffer.writeLongLE(wallet.cookieBalance) + } + } + + fun shopUnknownReply(): OutboundPacket { + return OutboundPacket { buffer -> + buffer.writeShortLE(0x20e) + buffer.writeIntLE(0) // maybe this is a timestamp to sync sales + buffer.writeIntLE(0) + } + } +} \ No newline at end of file diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/player/Equipment.kt b/game-server/src/main/kotlin/work/fking/pangya/game/player/Equipment.kt index 61c18f2..76038bd 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/player/Equipment.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/player/Equipment.kt @@ -1,9 +1,9 @@ package work.fking.pangya.game.player import io.netty.buffer.ByteBuf -import work.fking.pangya.game.model.IFF_TYPE_BALL -import work.fking.pangya.game.model.IFF_TYPE_CLUBSET -import work.fking.pangya.game.model.IFF_TYPE_EQUIPITEM_ITEM +import work.fking.pangya.game.model.IffType.BALL +import work.fking.pangya.game.model.IffType.CLUBSET +import work.fking.pangya.game.model.IffType.EQUIPITEM_ITEM import work.fking.pangya.game.model.iffTypeFromId const val EQUIPPED_ITEMS_SIZE = 10 @@ -28,18 +28,18 @@ class Equipment( require(itemIffIds.size == EQUIPPED_ITEMS_SIZE) { "Equipped item iff ids invalid length" } for (i in itemIffIds.indices) { val iffId = itemIffIds[i] - require(iffId == 0 || iffTypeFromId(iffId) == IFF_TYPE_EQUIPITEM_ITEM) { "Iff object $iffId is not an item" } + require(iffId == 0 || iffTypeFromId(iffId) == EQUIPITEM_ITEM) { "Iff object $iffId is not an item" } this.itemIffIds[i] = iffId } } fun equipClubSet(clubSet: Item) { - require(clubSet.iffTypeId() == IFF_TYPE_CLUBSET) { "Item is not a clubSet" } + require(clubSet.iffTypeId() == CLUBSET) { "Item is not a clubSet" } clubSetUid = clubSet.uid } fun equipComet(comet: Item) { - require(comet.iffTypeId() == IFF_TYPE_BALL) { "Item is not a comet" } + require(comet.iffTypeId() == BALL) { "Item is not a comet" } cometIffId = comet.iffId } diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/player/Item.kt b/game-server/src/main/kotlin/work/fking/pangya/game/player/Item.kt index 6a2a173..6a4ca3e 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/player/Item.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/player/Item.kt @@ -1,10 +1,10 @@ package work.fking.pangya.game.player import io.netty.buffer.ByteBuf -import work.fking.pangya.game.model.IFF_TYPE_BALL -import work.fking.pangya.game.model.IFF_TYPE_EQUIPITEM_ITEM -import work.fking.pangya.game.model.IFF_TYPE_NOEQUIP_ITEM import work.fking.pangya.game.model.IffObject +import work.fking.pangya.game.model.IffType.BALL +import work.fking.pangya.game.model.IffType.EQUIPITEM_ITEM +import work.fking.pangya.game.model.IffType.NOEQUIP_ITEM import work.fking.pangya.game.model.iffTypeFromId class Item( @@ -19,7 +19,7 @@ class Item( fun quantifiable(): Boolean { return when (iffTypeFromId(iffId)) { - IFF_TYPE_EQUIPITEM_ITEM, IFF_TYPE_NOEQUIP_ITEM, IFF_TYPE_BALL -> true + EQUIPITEM_ITEM, NOEQUIP_ITEM, BALL -> true else -> false } } diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/task/ChangeClubSetStatTask.kt b/game-server/src/main/kotlin/work/fking/pangya/game/task/ChangeClubSetStatTask.kt index 869bd73..c3840bb 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/task/ChangeClubSetStatTask.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/task/ChangeClubSetStatTask.kt @@ -1,6 +1,6 @@ package work.fking.pangya.game.task -import work.fking.pangya.game.model.IFF_TYPE_CLUBSET +import work.fking.pangya.game.model.IffType.CLUBSET import work.fking.pangya.game.model.iffTypeFromId import work.fking.pangya.game.packet.outbound.ClubSetReplies import work.fking.pangya.game.packet.outbound.ClubSetReplies.UpgradeResult.CANNOT_DOWNGRADE_ANYMORE @@ -31,7 +31,7 @@ class ChangeClubSetStatTask( override fun run() { val clubSet = player.inventory.findByUid(itemUid) ?: throw IllegalStateException("Player ${player.nickname} tried to upgrade an item it does not own") - require(iffTypeFromId(clubSet.iffId) == IFF_TYPE_CLUBSET) { "Player ${player.nickname} tried to upgrade an item that is not a ClubSet" } + require(iffTypeFromId(clubSet.iffId) == CLUBSET) { "Player ${player.nickname} tried to upgrade an item that is not a ClubSet" } when (type) { TYPE_UPGRADE_CLUBSET -> upgrade(clubSet) diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCharacterTask.kt b/game-server/src/main/kotlin/work/fking/pangya/game/task/ShopUnlockCharacterTask.kt similarity index 73% rename from game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCharacterTask.kt rename to game-server/src/main/kotlin/work/fking/pangya/game/task/ShopUnlockCharacterTask.kt index d4e3808..b397707 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCharacterTask.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/task/ShopUnlockCharacterTask.kt @@ -1,20 +1,21 @@ package work.fking.pangya.game.task -import work.fking.pangya.game.model.IFF_TYPE_CHARACTER +import work.fking.pangya.game.model.IffType.CHARACTER import work.fking.pangya.game.model.iffTypeFromId +import work.fking.pangya.game.packet.outbound.ShopReplies import work.fking.pangya.game.persistence.PersistenceContext import work.fking.pangya.game.player.Character import work.fking.pangya.game.player.Player import work.fking.pangya.game.player.characterBaseParts -class UnlockCharacterTask( +class ShopUnlockCharacterTask( private val persistenceCtx: PersistenceContext, private val player: Player, private val iffId: Int ) : Runnable { override fun run() { - require(iffTypeFromId(iffId) == IFF_TYPE_CHARACTER) { "iffId is not a character" } + require(iffTypeFromId(iffId) == CHARACTER) { "iffId is not a character" } val partIffIds = characterBaseParts(iffId) val character = persistenceCtx.characterRepository.save( txCtx = persistenceCtx.noTxContext(), @@ -25,5 +26,6 @@ class UnlockCharacterTask( ) ) player.characterRoster.entries.add(character) + player.writeAndFlush(ShopReplies.purchaseSuccessful(player.wallet)) } } \ No newline at end of file diff --git a/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCaddieTask.kt b/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCaddieTask.kt index 325b69a..cd1bee1 100644 --- a/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCaddieTask.kt +++ b/game-server/src/main/kotlin/work/fking/pangya/game/task/UnlockCaddieTask.kt @@ -1,6 +1,6 @@ package work.fking.pangya.game.task -import work.fking.pangya.game.model.IFF_TYPE_CADDIE +import work.fking.pangya.game.model.IffType.CADDIE import work.fking.pangya.game.model.iffTypeFromId import work.fking.pangya.game.persistence.PersistenceContext import work.fking.pangya.game.player.Caddie @@ -13,7 +13,7 @@ class UnlockCaddieTask( ) : Runnable { override fun run() { - require(iffTypeFromId(iffId) == IFF_TYPE_CADDIE) { "iffId is not a caddie" } + require(iffTypeFromId(iffId) == CADDIE) { "iffId is not a caddie" } val caddie = Caddie(iffId = iffId) val persistedCaddie = persistenceCtx.caddieRepository.save(persistenceCtx.noTxContext(), player.uid, caddie) player.caddieRoster.entries.add(persistedCaddie)