GameStateProvider.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { createContext, useContext, useEffect, useState } from "react"
  2. import { PublicKey } from "@solana/web3.js"
  3. import { useConnection, useWallet } from "@solana/wallet-adapter-react"
  4. import {
  5. program,
  6. PlayerData,
  7. MAX_ENERGY,
  8. TIME_TO_REFILL_ENERGY,
  9. GameData,
  10. GAME_DATA_SEED,
  11. } from "@/utils/anchor"
  12. import { BN } from "@coral-xyz/anchor"
  13. const GameStateContext = createContext<{
  14. playerDataPDA: PublicKey | null
  15. gameState: PlayerData | null
  16. nextEnergyIn: number
  17. totalWoodAvailable: number | null
  18. }>({
  19. playerDataPDA: null,
  20. gameState: null,
  21. nextEnergyIn: 0,
  22. totalWoodAvailable: 0
  23. })
  24. export const useGameState = () => useContext(GameStateContext)
  25. export const GameStateProvider = ({
  26. children,
  27. }: {
  28. children: React.ReactNode
  29. }) => {
  30. const { publicKey } = useWallet()
  31. const { connection } = useConnection()
  32. const [playerDataPDA, setPlayerData] = useState<PublicKey | null>(null)
  33. const [playerState, setPlayerState] = useState<PlayerData | null>(null)
  34. const [timePassed, setTimePassed] = useState<any>([])
  35. const [nextEnergyIn, setEnergyNextIn] = useState<number>(0)
  36. const [gameDataPDA, setGameDataPDA] = useState<PublicKey | null>(null)
  37. const [gameData, setGameData] = useState<GameData | null>(null)
  38. const [totalWoodAvailable, setTotalWoodAvailable] = useState<number | null>(0)
  39. useEffect(() => {
  40. setPlayerState(null)
  41. if (!publicKey) {
  42. return
  43. }
  44. const [pda] = PublicKey.findProgramAddressSync(
  45. [Buffer.from("player", "utf8"), publicKey.toBuffer()],
  46. program.programId
  47. )
  48. setPlayerData(pda)
  49. program.account.playerData
  50. .fetch(pda)
  51. .then((data) => {
  52. setPlayerState(data)
  53. })
  54. .catch((error) => {
  55. window.alert("No player data found, please init!")
  56. })
  57. connection.onAccountChange(pda, (account) => {
  58. setPlayerState(program.coder.accounts.decode("playerData", account.data))
  59. })
  60. }, [publicKey])
  61. useEffect(() => {
  62. setGameData(null)
  63. if (!publicKey) {
  64. return
  65. }
  66. const [pda] = PublicKey.findProgramAddressSync(
  67. [Buffer.from(GAME_DATA_SEED, "utf8")],
  68. program.programId
  69. )
  70. setGameDataPDA(gameDataPDA)
  71. program.account.gameData
  72. .fetch(pda)
  73. .then((data) => {
  74. setGameData(data)
  75. setTotalWoodAvailable(data.totalWoodCollected.toNumber());
  76. })
  77. .catch((error) => {
  78. window.alert("No game data found, please init!")
  79. })
  80. connection.onAccountChange(pda, (account) => {
  81. const newGameData = program.coder.accounts.decode("gameData", account.data)
  82. setGameData(newGameData);
  83. setTotalWoodAvailable(newGameData.totalWoodCollected.toNumber());
  84. })
  85. }, [publicKey])
  86. useEffect(() => {
  87. const interval = setInterval(async () => {
  88. if (
  89. playerState == null ||
  90. playerState.lastLogin == undefined ||
  91. playerState.energy.toNumber() >= MAX_ENERGY
  92. ) {
  93. return;
  94. }
  95. const lastLoginTime = playerState.lastLogin.toNumber() * 1000;
  96. const currentTime = Date.now();
  97. let timePassed = (currentTime - lastLoginTime) / 1000;
  98. while (timePassed >= TIME_TO_REFILL_ENERGY.toNumber() && playerState.energy.toNumber() < MAX_ENERGY) {
  99. playerState.energy = playerState.energy.add(new BN(1));
  100. playerState.lastLogin = playerState.lastLogin.add(TIME_TO_REFILL_ENERGY);
  101. timePassed -= TIME_TO_REFILL_ENERGY.toNumber();
  102. }
  103. setTimePassed(timePassed);
  104. const nextEnergyIn = Math.floor(TIME_TO_REFILL_ENERGY.toNumber() - timePassed);
  105. setEnergyNextIn(nextEnergyIn > 0 ? nextEnergyIn : 0);
  106. }, 1000);
  107. return () => clearInterval(interval)
  108. }, [playerState, timePassed, nextEnergyIn])
  109. return (
  110. <GameStateContext.Provider
  111. value={{
  112. playerDataPDA,
  113. gameState: playerState,
  114. nextEnergyIn,
  115. totalWoodAvailable,
  116. }}
  117. >
  118. {children}
  119. </GameStateContext.Provider>
  120. )
  121. }