Как решить Quasar: метод не найден в ошибке класса, связанной с ключевым словом Kotlin copy? - PullRequest
0 голосов
/ 19 июня 2019

Я сталкиваюсь со следующей ошибкой при запуске потока для обновления состояния с использованием вспомогательного метода и метода копирования Kotlin.

Это сообщение об ошибке + трассировка стека, которую я получаю, когдазапустить поток:

java.lang.NoSuchMethodError: @com.contractsAndStates.states.GameBoardState.<init>(ZLjava/util/List;Ljava/util/List;Ljava/util/List;Lnet/corda/core/contracts/UniqueIdentifier;Ljava/util/List;Ljava/util/List;ZILnet/corda/core/identity/Party;Lnet/corda/core/contracts/UniqueIdentifier;ILkotlin/jvm/internal/DefaultConstructorMarker;)V

	at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:250)
	at com.flows.SetupGameBoardFlow.call(SetupGameBoardFlow.kt:34)
	at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:228)
	at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:45)
	at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
	at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
	at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
	at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63)

Я также получаю странные ошибки в консоли, которые указывают на то, что эта проблема имеет какое-то отношение к конструктору копирования:

[quasar] WARNING: Method not found in class - assuming suspendable: com/contractsAndStates/states/GameBoardState#copy$default(Lcom/contractsAndStates/states/GameBoardState;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Lnet/corda/core/contracts/UniqueIdentifier;Ljava/util/List;Ljava/util/List;ZILnet/corda/core/identity/Party;Lnet/corda/core/contracts/UniqueIdentifier;ILjava/lang/Object;)Lcom/contractsAndStates/states/GameBoardState; (at BuildInitialSettlementAndRoadFlow.kt:com/flows/BuildInitialSettlementAndRoadFlow#call)
[quasar] WARNING: Method not found in class - assuming suspendable: com/contractsAndStates/states/GameBoardState#copy$default(Lcom/contractsAndStates/states/GameBoardState;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Lnet/corda/core/contracts/UniqueIdentifier;Ljava/util/List;Ljava/util/List;ZILnet/corda/core/identity/Party;Lnet/corda/core/contracts/UniqueIdentifier;ILjava/lang/Object;)Lcom/contractsAndStates/states/GameBoardState; (at BuildRoadFlow.kt:com/flows/BuildRoadFlow#call)
[quasar] WARNING: Method not found in class - assuming suspendable: com/contractsAndStates/states/GameBoardState#copy$default(Lcom/contractsAndStates/states/GameBoardState;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Lnet/corda/core/contracts/UniqueIdentifier;Ljava/util/List;Ljava/util/List;ZILnet/corda/core/identity/Party;Lnet/corda/core/contracts/UniqueIdentifier;ILjava/lang/Object;)Lcom/contractsAndStates/states/GameBoardState; (at BuildSettlementFlow.kt:com/flows/BuildSettlementFlow#call)

Это мое состояние:

@CordaSerializable
@BelongsToContract(GameStateContract::class)
data class GameBoardState(val beginner: Boolean = false,
                      val hexTiles: MutableList<HexTile>,
                      val ports: List<Port>,
                      val players: List<Party>,
                      val turnTrackerLinearId: UniqueIdentifier,
                      val robberLinearId: UniqueIdentifier,
                      val spectators: List<Party> = listOf(),
                      val settlementsPlaced: MutableList<MutableList<Boolean>> = MutableList(19) { MutableList(6) { false } },
                      var setUpComplete: Boolean = false,
                      var initialPiecesPlaced: Int = 0,
                      val winner: Party? = null,
                      override val linearId: UniqueIdentifier = UniqueIdentifier()): LinearState {

override val participants: List<Party> get() = players

fun updateHexTiles(updatedHexTiles: MutableList<HexTile>): 
GameBoardState = copy(hexTiles = updatedHexTiles)

fun updateSettlementsPlaced(updatedSettlementsPlaced: 
MutableList<MutableList<Boolean>>): GameBoardState = copy(settlementsPlaced = updatedSettlementsPlaced)

fun weWin(ourIdentity: Party): GameBoardState = copy(winner = ourIdentity)

}

И это мой поток:

// *******************
// * Game Start Flow *
// *******************

/**
 * This is the first flow run by a given node in order to propose the start of a new game to other
 * nodes on the network. In its current implementation the flow requires the specification of which
 * parties will participate in the game. In the future there may be an opportunity to handle this auto-
 * matically through match making functionality.
 */

// TODO: Make this flow testable by separating GameBoard generation functionality from flow logic.

@InitiatingFlow(version = 1)
@StartableByRPC
class SetupGameBoardFlow(val p1: Party, val p2: Party, val p3: Party, val p4: Party) : FlowLogic<SignedTransaction>() {

    companion object {
        object GETTING_NOTARY : ProgressTracker.Step("Getting reference to the notary")
        object INITIALIZING_TRANSACTION : ProgressTracker.Step("Initializing the transaction and transaction builder")
        object ISSUING_COMMANDS : ProgressTracker.Step("Issuing the appropriate commands")
        object CREATING_A_TURN_TRACKER : ProgressTracker.Step("Creating a turn tracker for you buncha' cheaters")
        object SETTING_UP_YOUR_GAMEBOARD : ProgressTracker.Step("Setting up your personal GameBoard on Corda")
        object FINALIZING_GAMEBOARD : ProgressTracker.Step("Finalizing your GameBoard on Corda")
        object ADDING_PORTS_FOR_YOU_SEAFARING_SOULS : ProgressTracker.Step("Adding sea-ports for your sea-faring souls")
        object FINDING_A_VILLAIN_TO_PLAY_THE_ROBBER : ProgressTracker.Step("Finding a villain to play the robber")
        object ADDING_ALL_GAME_STATES_TO_THE_TRANSACTION : ProgressTracker.Step("Adding all states to the transaction")
        object VERIFYING : ProgressTracker.Step("Verifying the transaction")
        object COLLECTING_SIGNATURES : ProgressTracker.Step("Collecting signatures from your fellow citizens of Cordan")
        object FINALIZING_TRANSACTION : ProgressTracker.Step("Finalizing the transaction")
    }

    override val progressTracker = ProgressTracker(
            GETTING_NOTARY,
            INITIALIZING_TRANSACTION,
            ISSUING_COMMANDS,
            CREATING_A_TURN_TRACKER,
            SETTING_UP_YOUR_GAMEBOARD,
            ADDING_PORTS_FOR_YOU_SEAFARING_SOULS,
            FINALIZING_GAMEBOARD,
            ADDING_ALL_GAME_STATES_TO_THE_TRANSACTION,
            FINDING_A_VILLAIN_TO_PLAY_THE_ROBBER,
            VERIFYING,
            COLLECTING_SIGNATURES,
            FINALIZING_TRANSACTION
    )

    @Suspendable
    override fun call(): SignedTransaction {

        /**
         * The following objects define all of the steps required to execute the flow. These steps will
         * be executed in sequence to set up a game board and displayed to the user via a progress tracker.
         */
        // Step 1. Get a reference to the notary service on the network
        progressTracker.currentStep = GETTING_NOTARY
        val notary = serviceHub.networkMapCache.notaryIdentities.first()

        // Step 2. Create a new transaction builder
        progressTracker.currentStep = INITIALIZING_TRANSACTION
        val tb = TransactionBuilder(notary)

        // Step 3. Create a new issue command and add it to the transaction.
        val playerKeys = listOf(p1.owningKey, p2.owningKey, p3.owningKey, p4.owningKey)
        progressTracker.currentStep = ISSUING_COMMANDS
        val issueCommand = Command(GameStateContract.Commands.SetUpGameBoard(), playerKeys)
        val createTurnTracker = Command(TurnTrackerContract.Commands.CreateTurnTracker(), playerKeys)
        tb.addCommand(issueCommand)
        tb.addCommand(createTurnTracker)

        // Step 4. Create a new turn tracker state
        progressTracker.currentStep = CREATING_A_TURN_TRACKER
        val turnTrackerState = TurnTrackerState(participants = listOf(p1, p2, p3, p4))

        // Step 5. Generate data for new game state
        progressTracker.currentStep = SETTING_UP_YOUR_GAMEBOARD

        // Storage for hexTiles that will be randomly generated.
        val hexTiles = arrayListOf<HexTile>()

        // Available Terrain types which will determine resources generated by hex tiles.
        val terrainTypes: Array<String> = arrayOf("Forest", "Pasture", "Field", "Hill", "Mountain", "Desert")

        /**
         * Ports in Settlers of Cordan enable users to exchange resources at more favourable rates than those available to players generally.
         * To access a port, a player must have previously built a settlement on a hex tile with an adjacent port. The settlement must also
         * be built on one of the designated access point specified below.
         */

        // Available Port Tiles
        progressTracker.currentStep = ADDING_PORTS_FOR_YOU_SEAFARING_SOULS
        progressTracker.nextStep()
        val portTilesTracking = BooleanArray(9)
        val portTiles: ArrayList<PortTile> = arrayListOf(
                PortTile(listOf(2.Sheep), listOf(1.Wood, 1.Brick, 1.Ore, 1.Wheat)),
                PortTile(listOf(2.Wood), listOf(1.Sheep, 1.Brick, 1.Ore, 1.Wheat)),
                PortTile(listOf(2.Brick), listOf(1.Wood, 1.Sheep, 1.Ore, 1.Wheat)),
                PortTile(listOf(2.Ore), listOf(1.Wood, 1.Sheep, 1.Brick, 1.Wheat)),
                PortTile(listOf(2.Wheat), listOf(1.Wood, 1.Sheep, 1.Brick, 1.Sheep)),
                PortTile(listOf(3.Wheat, 3.Wood, 3.Sheep, 3.Brick, 3.Sheep), listOf(1.Wheat, 1.Wood, 1.Sheep, 1.Brick, 1.Sheep)),
                PortTile(listOf(3.Wheat, 3.Wood, 3.Sheep, 3.Brick, 3.Sheep), listOf(1.Wheat, 1.Wood, 1.Sheep, 1.Brick, 1.Sheep)),
                PortTile(listOf(3.Wheat, 3.Wood, 3.Sheep, 3.Brick, 3.Sheep), listOf(1.Wheat, 1.Wood, 1.Sheep, 1.Brick, 1.Sheep)),
                PortTile(listOf(3.Wheat, 3.Wood, 3.Sheep, 3.Brick, 3.Sheep), listOf(1.Wheat, 1.Wood, 1.Sheep, 1.Brick, 1.Sheep))
        )

        val portHexTileAccessPointMapping = arrayListOf(
                listOf(AccessPoint(0, listOf(5, 1))),
                listOf(AccessPoint(1, listOf(0, 2)), AccessPoint(2, listOf(5))),
                listOf(AccessPoint(2, listOf(2)), AccessPoint(6, listOf(0, 1))),
                listOf(AccessPoint(11, listOf(1, 2))),
                listOf(AccessPoint(15, listOf(2, 3)), AccessPoint(18, listOf(1))),
                listOf(AccessPoint(18, listOf(4)), AccessPoint(17, listOf(2, 3))),
                listOf(AccessPoint(16, listOf(3, 4))),
                listOf(AccessPoint(12, listOf(4, 5)), AccessPoint(7, listOf(3))),
                listOf(AccessPoint(3, listOf(4, 5)), AccessPoint(7, listOf(0)))
        )

        val ports: ArrayList<Port> = arrayListOf()

        for (i in 0..8) {
            var currPortTileIndex = Math.floor(Math.random() * (portTiles.size)).toInt()
            while (portTilesTracking[currPortTileIndex]) {
                currPortTileIndex = Math.floor(Math.random() * (portTiles.size)).toInt()
            }

            val currPortTile = portTiles[currPortTileIndex]
            ports.add(i, Port(currPortTile, portHexTileAccessPointMapping[i]))
        }

        /**
         * Role trigger tiles are placed on hexTiles to denote the dice role that gives the player the right to harvest a resource on a given turn.
         * These are placed in counter-clockwise order, start from the top left corner of the game board.
         */
        val roleTriggerTilePlacementMapping: Map<Int, Int> = mapOf(
                0 to 5, 1 to 2, 2 to 6, 3 to 3, 4 to 8, 5 to 10, 6 to 9, 7 to 12, 8 to 11, 9 to 4,
                10 to 8, 11 to 10, 12 to 9, 13 to 4, 14 to 5, 15 to 6, 16 to 3, 17 to 11
        )

        /**
         * Order in which rollTriggerTiles are placed on the existing hex tiles (translating counterclockwise placement to row-by-row hexTile ordering)
         */
        val roleTriggerTilePlacementOrder = arrayListOf(0, 11, 10, 1, 12, 17, 9, 2, 13, 18, 16, 8, 3, 14, 15, 7, 4, 5, 6)

        // Array with maximums for a specific type of HexTile that may be added to the game board.
        val checkArray = intArrayOf(4, 4, 4, 3, 3, 1)

        // Array with counters for specific types of HexTiles added to the game board.
        val countArray = intArrayOf(0, 0, 0, 0, 0, 0)

        // Index adjustment variable to account desert placement
        var desertSkippedIndexAdjustment = 0

        for (i in 0..18) {

            // Get a random index between 0 and 5 which will be used to access the HexTileTypes.
            var hexTypeIndex = (Math.random() * 6).toInt()

            // Check to ensure HexTiles selected so far do not exceed max of each type specified in checkArray.
            while (countArray[hexTypeIndex] >= checkArray[hexTypeIndex]) {
                hexTypeIndex = (Math.random() * 6).toInt()
            }

            // Get the hex resource type.
            val hexType = terrainTypes[hexTypeIndex]

            // Get the port (if relevant) to add to the HexTile


            // Create a HexTile to add to the gameboard.
            // Use role trigger placement mapping, role trigger placement order, and desertSkippedIndexAdjustment to ensure that role triggers
            // Are placed in the appropriate order.
            hexTiles.add(i, HexTile(
                    hexType,
                    if (hexType.equals("Desert")) 0 else roleTriggerTilePlacementMapping.getOrElse(roleTriggerTilePlacementOrder[i - desertSkippedIndexAdjustment]) { 0 },
                    terrainTypes[hexTypeIndex] == "Desert",
                    i))
            countArray[hexTypeIndex]++

            // Establish the index adjustment once a desert HexTile has been encountered.
            if (hexType.equals("Desert")) {
                desertSkippedIndexAdjustment = 1
            }
        }

        /**
         * Define the neighbouring hexTiles for each individual hexTile. In essence, we are creating a fully-connected graph modelling the state of the game board, this is necessary for a number of reasons,
         * including checking for valid placement of new roads and structures without forcing the user to provide unnecessarily specific input.
         */

        /**
         * TODO: Refactor to create a boolean mapping of settlements built with overlapping references such that building a settlement at coordinate 'n' on HexTile 'n'
         * causes the bool variable for the coordinate to flip from false to true, the bool variable is shared by hexTiles.
         */

        for (i in 0..2) {
            hexTiles[i].connect(3, hexTiles[ i + 3])
            hexTiles[i].connect(2, hexTiles[ i + 4])
            if (i != 2) hexTiles[i].connect(1, hexTiles[ i + 1])
        }

        for (i in 3..6) {
            hexTiles[i].connect(3, hexTiles[ i + 4])
            hexTiles[i].connect(2, hexTiles[ i + 5])
            if (i != 6) hexTiles[i].connect(1, hexTiles[ i + 1])
        }

        for (i in 7..11) {
            if (i != 6) hexTiles[i].connect(1, hexTiles[ i + 1])
        }

        for (i in 12..15) {
            hexTiles[i].connect(5, hexTiles[ i - 5])
            hexTiles[i].connect(0, hexTiles[ i - 4])
            if (i != 15) hexTiles[i].connect(1, hexTiles[ i + 1])
        }

        for (i in 16..18) {
            hexTiles[i].connect(5, hexTiles[ i - 4])
            hexTiles[i].connect(0, hexTiles[ i - 3])
            if (i != 18) hexTiles[i].connect(1, hexTiles[ i + 1])
        }

        // Step 5. Create a new game state
        // Randomize turn order
        val playersList = listOf(p1, p2, p3, p4)
        val randomizedPlayersList = arrayListOf<Party>()
        while (randomizedPlayersList.size < 4) {
            val randomNumber = (Math.random() * 3.99).toInt()
            if (!randomizedPlayersList.contains(playersList[randomNumber])) {
                randomizedPlayersList.add(playersList[randomNumber])
            }
        }

        // Step 6. Create a robber state and issueRobber commands - add both to the transaction
        progressTracker.currentStep = FINDING_A_VILLAIN_TO_PLAY_THE_ROBBER
        val hexTileWithDesert = hexTiles.filter { it.resourceType == "Desert" }.single()
        val robberState = RobberState(hexTileWithDesert.hexTileIndex, playersList)
        val createRobberCommand = Command(RobberContract.Commands.CreateRobber(), playerKeys)
        tb.addOutputState(robberState)
        tb.addCommand(createRobberCommand)

        progressTracker.currentStep = FINALIZING_GAMEBOARD
        val newGameState = GameBoardState(false, hexTiles, ports, randomizedPlayersList, turnTrackerState.linearId, robberState.linearId)

        // Step 7. Add the states to the transaction
        progressTracker.currentStep = ADDING_ALL_GAME_STATES_TO_THE_TRANSACTION
        tb.addOutputState(newGameState, GameStateContract.ID)
        tb.addOutputState(turnTrackerState, TurnTrackerContract.ID)

        // Step 8. Verify and sign the transaction
        progressTracker.currentStep = VERIFYING
        tb.verify(serviceHub)
        val ptx = serviceHub.signInitialTransaction(tb)

        // Step 8. Create a list of flows with the relevant participants
        progressTracker.currentStep = COLLECTING_SIGNATURES
        val sessions = (newGameState.participants - ourIdentity).map { initiateFlow(it) }.toSet()

        // Step 9. Collect other signatures
        val stx = subFlow(CollectSignaturesFlow(ptx, sessions))

        // Step 10. Run the FinalityFlow
        progressTracker.currentStep = FINALIZING_TRANSACTION

        val linearIDToBePrinted = newGameState.linearId
        val players = newGameState.players
        val playerNames = players.map { it.name.toString() }
        val currPlayer = players[0]

        // TODO: This messaging is not displaying
        System.out.println("\nYour unique game board identified is $linearIDToBePrinted")
        System.out.println("\nYou are playing with $playerNames")

        if (ourIdentity == currPlayer) {
            System.out.println("\nIt is your turn, you should use the BuildInitialSettlementAndRoadFlow to setup the board!")
        } else {
            System.out.println("\nIt is $currPlayer's turn")
        }

        return subFlow(FinalityFlow(stx, sessions))

    }
}

@InitiatedBy(SetupGameBoardFlow::class)
class SetupGameBoardFlowResponder(val counterpartySession: FlowSession) : FlowLogic<SignedTransaction>() {
    @Suspendable
    override fun call(): SignedTransaction {
        val signedTransactionFlow = object : SignTransactionFlow(counterpartySession) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                System.out.println("\nSomeone has invited you to play Settlers of Cordan (Catan on Corda)\n")
                val gameBoardState = stx.coreTransaction.outputsOfType<GameBoardState>().single()
                val linearIDToBePrinted = gameBoardState.linearId
                val players = gameBoardState.players
                val playerNames = players.map { it.name.toString() }
                val currPlayer = players[0]
                System.out.println("\nYour unique game board identified is $linearIDToBePrinted")
                System.out.println("\nYou are playing with $playerNames")

                if (ourIdentity == currPlayer) {
                    System.out.println("\nIt is your turn, you should use the BuildInitialSettlementAndRoadFlow to setup the board!")
                } else {
                    System.out.println("\nIt is $currPlayer's turn")
                }

            }
        }

        val txWeJustSignedId = subFlow(signedTransactionFlow)

        return subFlow(ReceiveFinalityFlow(otherSideSession = counterpartySession, expectedTxId = txWeJustSignedId.id))
    }
}
...