I have this code on PlayerJoinEvent to check if player exists on my database. If the player does exist, store the player's data into an array. If the player is new, create an account for the player. This works fine; however, whenever players join instantaneously, this code just return errors. Errors that seemingly pop up because I am managing my data incorrectly. . How can I prevent that? I was thinking of making some kind of query to manage the data of each players one by one, but i don't know how to do that. I am using libasyql. PHP: PlayerJoinEventpublic function accountExists ( Player $player ) { $this->connector->executeSelect( Queries::GET_ALL, [ "uuid" => (string)$player->getUniqueId(), ], function ( array $rows ) use ( $player ) : void { if ( isset( $rows[ 0 ] ) ) { $this->getManager()->storePlayerData( $player ); echo "exists"; } else { $this->createAccount( $player ); echo "not exists"; } } ); } PHP: /** * * Store player's data. This one is really important. * * @param Player $player * * @return bool */ public function storePlayerData ( Player $player ) { $uuid = (string)$player->getUniqueId(); $this->getPlugin()->getMySQLProvider()->loadPlayersData( $player, function ( array $data ) use ( $uuid, $player ) { foreach ($data as $_data) { $this->getPlayerData()->player_balance[ $uuid ] = $_data[ "BALANCE" ];//and many more such as kills, deaths, etc } } ); $event = new PlayerStoreDataEvent( $this->plugin, $player ); $this->getServer()->getPluginManager()->callEvent( $event ); $this->getPlugin()->getLogger()->info( "Stored data to array" ); return true; } PHP: /** * @param Player $player * @param int $defaultBalance * * @return bool */ public function createAccount ( Player $player, $defaultBalance = 0 ) { /** @var string $name */ $name = $player->getName(); /** @var UUID $uuid */ $uuid = $player->getUniqueId(); /** @var string $address */ $address = $player->getAddress(); if ( isset( $this->getManager()->existingPlayer[ (string)$uuid ] ) ) { return true; } $currentDate = date( 'Y-m-d H:i:s' ); $this->connector->executeInsert( Queries::CREATE_ACCOUNT, [ "uuid" => (string)$uuid, "name" => $name, "address" => $address, ] ); $this->getManager()->storePlayerData( $player ); $this->getPlugin()->getLogger()->info( "created account for " . $name ); return true; }
There are times when player data (from database) is not going into the array which results error when I try to use the player data from the array. I tried fixing this by making a check (if the player data is in the array already )before using the player data.
What exactly are the times you want to use the player data? Consider cancelling those events, ignoring those events or postponing the handling on a case by case basis.
Do other events depend on what you initialized in join event? If not, simply delay the event execution until data are ready. A promise like API would help, such as await-generator
For example, you can have a class like this for each player: PHP: class PlayerQueue { private $data; private $queue; public function onDataReady($data){ $this->data=$data; foreach($this->queue as $run) $run($data); } public function execute(callable $run){ if(isset($this->data)) $run($this->data); else $this->queue[] = $run; }} Pass [$queue, "onDataReady"] to libasynql's onSuccess function. Note that you should not mutate the $event object in the closure you pass to $queue->execute(), because the code may be executed *after* the event handler has completed, and mutating the event object (like cancelling events) won't do anything because its effects have already happened at this point. The best way is to cancel those events when player data are not ready, e.g. disallow player from chatting, moving, etc. if some permission checks requiring the data could not be performed yet. On the other hand, if your event handler doesn't need to mutate the event, e.g. when another player died and you want to check if this player is in the same team as the dead player (to receive notification), you could just ignore the event (or skip this player if it's in a loop). Or even better, if it's just an event handler for logging, you might just schedule (using the queue class above) to handle the event later. Think about auth plugins. Many actions cannot be done without the player authenticating first. This is essentially the same thing.