.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Free For All
4 | (FFA, or Free for All) is a survival and fighting game where the game begins and you have to fight to survive.
5 |
6 |
7 | # Dependencies
8 | - [BanCommands](https://poggit.pmmp.io/p/BanCommands)
9 |
10 | # How to create an arena
11 | - Teleport to the world who need to make an arena in it.
12 | - First, type `/ffa create "your arena name"` to create the arena.
13 | - Now go to the arena lobby and type `/ffa setlobby` to set it.
14 | - Okay, now go to the respawn position and type `/ffa setrespawn` that will return to it after death (you can turn it on or off from the config).
15 | - Use `/ffa reload` to load the arena.
16 | - Great, you are now ready to play. Type `/ffa join "your arena name."` enjoy. If you want to leave the game, type `/ffa quit'.
17 |
18 | # the configure
19 | - Formats
20 | - `{PLAYER}` : to get the player's name
21 | - `{ARENA} or {GAME}` : to get the arena name
22 | - `&` : same as `§`
23 | - `{WORLD}` : to get the arena's world name
24 | - `{PLAYERS}` : to get arena players count
25 | - `{TIME}` : to get player protected time left
26 | - General
27 | - `scoreboardIp` : You can set your server IP to show it on the game scoreboard.
28 | - `banned-commands`: You can add the commands you want banned in the game.
29 | - `death-respawn-inMap` : That will return the player to the respawn position after death; you can set it to `true` or `false`.
30 | - `join-and-respawn-protected` : that will protect the player for 3 seconds after joining and respawning.
31 | - `protected-time` : to edit the protected time.
32 | - `protected-message` : to edit protect message.
33 | - `death-attack-message` : Here, you can set the death message when killed by someone.
34 | - `death-void-message` : and here you can set the death message when killed by void.
35 | - `join-message` : to edit player join message.
36 | - `leave-message` : to edit player leave message
37 | - `kills-messages` : To add or remove kill messages, this message will be sent to the player every 5 kills automatically.
38 | - `scoreboard-title` : to edit the scoreboard title name.
39 | - `provider` : Currently, it's supported `sqlite3` only; do not change it.
40 | - `database` : Do not change anything.
41 | - `kits` : You can edit the default kit right now, for example:
42 | ```yaml
43 | kits:
44 | default:
45 | slot-0:
46 | id: iron_sword
47 | count: 1
48 | enchants: []
49 | slot-1:
50 | id: golden_apple
51 | count: 5
52 | enchants: []
53 | slot-2:
54 | id: bow
55 | count: 1
56 | enchants: []
57 | slot-3:
58 | id: arrow
59 | count: 15
60 | enchants: []
61 | helmet:
62 | id: iron_helmet
63 | enchants: []
64 | chestplate:
65 | id: iron_chestplate
66 | enchants:
67 | id-0:
68 | level: 2
69 | leggings:
70 | id: iron_leggings
71 | enchants: []
72 | boots:
73 | id: iron_boots
74 | enchants: []
75 | ```
76 |
77 | # Commands
78 | Command | Description | Permission
79 | --- | --- | ---
80 | `/ffa join ` | `To join a specific or random arena` | `No permission`
81 | `/ffa quit` | `To leave the arena` | `No permission`
82 | `/ffa help` | `To see the command list` | `ffa.command.admin`
83 | `/ffa create` | `To create a new arena` | `ffa.command.admin`
84 | `/ffa remove` | `To delete a specific arena` | `ffa.command.admin`
85 | `/ffa setlobby` | `To set the lobby position in the arena` | `ffa.command.admin`
86 | `/ffa setrespawn` | `To set the respawn position in the arena` | `ffa.command.admin`
87 | `/ffa reload` | `re-loaded the kits and arenas` | `ffa.command.admin`
88 | `/ffa list` | `To see the arenas list` | `ffa.command.admin`
89 |
90 | # API
91 | As of v2.0.0, all the API functions have moved to [API](https://github.com/Laith98Dev/FFA/blob/main/src/Laith98Dev/FFA/API.php).
92 | ```php
93 | use Laith98Dev\FFA\API;
94 | use Laith98Dev\FFA\utils\ClosureResult;
95 |
96 | // add kills to the player
97 | API::addKill($PlayerOrPlayerName, $amount, function (ClosureResult $result){
98 | if($result->getStatus() == ClosureResult::STATE_SUCCESS){
99 | echo "Added `$amount` kills to the player successfully." . PHP_EOL;
100 | } else {
101 | echo "Failed to add kills to the player; reason: " . $result->getValue() . PHP_EOL;
102 | }
103 | });
104 |
105 | // add deaths to player
106 | API::addDeath($PlayerOrPlayerName, $amount, function (ClosureResult $result){
107 | if($result->getStatus() == ClosureResult::STATE_SUCCESS){
108 | echo "Added `$amount` deaths to the player successfully." . PHP_EOL;
109 | } else {
110 | echo "Failed to add deaths to the player; reason: " . $result->getValue() . PHP_EOL;
111 | }
112 | });
113 |
114 | // get player kills
115 | API::getKills($PlayerOrPlayerName, function (ClosureResult $result){
116 | if($result->getStatus() == ClosureResult::STATE_SUCCESS){
117 | echo "Player kills is " . $result->getValue() . PHP_EOL;
118 | } else {
119 | echo "Failed to get player kills; reason: " . $result->getValue() . PHP_EOL;
120 | }
121 | });
122 |
123 | // get player deaths
124 | API::getDeaths($PlayerOrPlayerName, function (ClosureResult $result){
125 | if($result->getStatus() == ClosureResult::STATE_SUCCESS){
126 | echo "Player deaths is " . $result->getValue() . PHP_EOL;
127 | } else {
128 | echo "Failed to get player deaths; reason: " . $result->getValue() . PHP_EOL;
129 | }
130 | });
131 |
132 | // Check if an arena exists
133 | API::isValidArena($arena_name, function (ClosureResult $result){
134 | if($result->getValue()){
135 | echo "Arena exists" . PHP_EOL;
136 | } else {
137 | echo "Arena doesn't exist." . PHP_EOL;
138 | }
139 | });
140 |
141 | ```
142 |
143 | # Other
144 | - [](https://www.youtube.com/watch?v=SwzWwsrGG74&ab_channel=LaithYoutuber)
145 | - [](https://paypal.me/Laith113)
146 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Laith98Dev/FFA/61d3604a5868861fd37e319dc1384149ba19c79d/icon.png
--------------------------------------------------------------------------------
/plugin.yml:
--------------------------------------------------------------------------------
1 | name: FFA
2 | api: 5.0.0
3 | version: 2.0.0
4 | main: Laith98Dev\FFA\Main
5 | author: Laith98Dev
6 | website: https://github.com/Laith98Dev
7 | depend: BanCommands
8 |
9 | permissions:
10 | ffa.command:
11 | default: true
12 | ffa.command.admin:
13 | default: op
--------------------------------------------------------------------------------
/resources/sqlite.sql:
--------------------------------------------------------------------------------
1 | -- #!sqlite
2 | -- #{ffa
3 | -- # {init
4 | CREATE TABLE IF NOT EXISTS players (
5 | player VARCHAR NOT NULL PRIMARY KEY,
6 | kills INT NOT NULL,
7 | deaths INT NOT NULL
8 | );
9 | -- # &
10 | CREATE TABLE IF NOT EXISTS arenas (
11 | name VARCHAR NOT NULL PRIMARY KEY,
12 | world VARCHAR NOT NULL,
13 | lobby VARCHAR NOT NULL,
14 | respawn VARCHAR NOT NULL
15 | );
16 | -- # }
17 | -- # {get-arena
18 | -- # :name string
19 | SELECT * FROM arenas WHERE name = :name;
20 | -- # }
21 | -- # {get-arenas
22 | SELECT * FROM arenas;
23 | -- # }
24 | -- # {add-arena
25 | -- # :name string
26 | -- # :world string
27 | -- # :lobby string
28 | -- # :respawn string
29 | INSERT INTO arenas(name, world, lobby, respawn) VALUES ( :name , :world, :lobby, :respawn);
30 | -- # }
31 | -- # {delete-arena
32 | -- # :name string
33 | DELETE FROM arenas WHERE name = :name;
34 | -- # }
35 | -- # {update-lobby
36 | -- # :name string
37 | -- # :lobby string
38 | UPDATE arenas SET lobby = :lobby WHERE name = :name;
39 | -- # }
40 | -- # {update-respawn
41 | -- # :name string
42 | -- # :respawn string
43 | UPDATE arenas SET respawn = :respawn WHERE name = :name;
44 | -- # }
45 | -- # {add-kills
46 | -- # :player string
47 | -- # :kills int
48 | INSERT INTO players(player, kills, deaths) VALUES ( :player , 1, 0 )
49 | ON CONFLICT(player) DO UPDATE SET kills = kills + :kills WHERE player = :player;
50 | -- # }
51 | -- # {add-deaths
52 | -- # :player string
53 | -- # :deaths int
54 | INSERT INTO players(player, kills, deaths) VALUES ( :player , 0, 1 )
55 | ON CONFLICT(player) DO UPDATE SET deaths = deaths + :deaths WHERE player = :player;
56 | -- # }
57 | -- # {get-kills
58 | -- # :player string
59 | SELECT kills FROM players WHERE player = :player;
60 | -- # }
61 | -- # {get-deaths
62 | -- # :player string
63 | SELECT deaths FROM players WHERE player = :player;
64 | -- # }
65 | -- #}
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/API.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Closure;
41 | use Laith98Dev\FFA\utils\ClosureResult;
42 | use Laith98Dev\FFA\utils\SQLKeyStorer;
43 | use pocketmine\player\Player;
44 | use poggit\libasynql\SqlError;
45 |
46 | class API
47 | {
48 | /**
49 | * This function allows you to add a specific count of kills to a specific player.
50 | *
51 | * @param Player|string $player
52 | * @param integer $amount
53 | * @param Closure|null $onSuccess : function(ClosureResult $result) : void{}
54 | * @return void
55 | */
56 | public static function addKill(Player|string $player, int $amount, Closure $onSuccess): void{
57 | $name = $player instanceof Player ? $player->getName() : $player;
58 | Main::getInstaance()->getProvider()->db()->executeChange(
59 | SQLKeyStorer::ADD_KILLS,
60 | [
61 | "player" => $name,
62 | "kills" => $amount,
63 | ],
64 | fn(int $affectedRows) => $onSuccess(
65 | ClosureResult::create(
66 | ClosureResult::STATE_SUCCESS,
67 | $affectedRows
68 | )
69 | ),
70 | fn(SqlError $err) => $onSuccess(
71 | ClosureResult::create(
72 | ClosureResult::STATE_FAILURE,
73 | $err->getMessage()
74 | )
75 | )
76 | );
77 | }
78 |
79 | /**
80 | * This function allows you to add a specific count of deaths to a specific player.
81 | *
82 | * @param Player|string $player
83 | * @param integer $amount
84 | * @param Closure $onSuccess : function(ClosureResult $result) : void{}
85 | * @return void
86 | */
87 | public static function addDeath(Player|string $player, int $amount, Closure $onSuccess){
88 | $name = $player instanceof Player ? $player->getName() : $player;
89 | Main::getInstaance()->getProvider()->db()->executeChange(
90 | SQLKeyStorer::ADD_DEATHS,
91 | [
92 | "player" => $name,
93 | "deaths" => $amount,
94 | ],
95 | fn(int $affectedRows) => $onSuccess(
96 | ClosureResult::create(
97 | ClosureResult::STATE_SUCCESS,
98 | $affectedRows
99 | )
100 | ),
101 | fn(SqlError $err) => $onSuccess(
102 | ClosureResult::create(
103 | ClosureResult::STATE_FAILURE,
104 | $err->getMessage()
105 | )
106 | )
107 | );
108 | }
109 |
110 | /**
111 | * This function allows you to get the kill count of a specific player.
112 | *
113 | * @param Player|string $player
114 | * @param Closure $onSuccess : function(ClosureResult $result) : void{}
115 | * @return void
116 | */
117 | public static function getKills(Player|string $player, Closure $onSuccess){
118 | $name = $player instanceof Player ? $player->getName() : $player;
119 | Main::getInstaance()->getProvider()->db()->executeSelect(
120 | SQLKeyStorer::GET_KILLS,
121 | [
122 | "player" => $name
123 | ],
124 | fn(array $rows) => $onSuccess(
125 | ClosureResult::create(
126 | ClosureResult::STATE_SUCCESS,
127 | (isset($rows[0]) ? $rows[0]["kills"] : 0)
128 | )
129 | ),
130 | fn(SqlError $err) => $onSuccess(
131 | ClosureResult::create(
132 | ClosureResult::STATE_FAILURE,
133 | $err->getMessage()
134 | )
135 | )
136 | );
137 | }
138 |
139 | /**
140 | * This function allows you to get the death count of a specific player.
141 | *
142 | * @param Player|string $player
143 | * @param Closure $onSuccess : function(ClosureResult $result) : void{}
144 | * @return void
145 | */
146 | public static function getDeaths(Player|string $player, Closure $onSuccess){
147 | $name = $player instanceof Player ? $player->getName() : $player;
148 | Main::getInstaance()->getProvider()->db()->executeSelect(
149 | SQLKeyStorer::GET_DEATHS,
150 | [
151 | "player" => $name
152 | ],
153 | fn(array $rows) => $onSuccess(
154 | ClosureResult::create(
155 | ClosureResult::STATE_SUCCESS,
156 | (isset($rows[0]) ? $rows[0]["deaths"] : 0)
157 | )
158 | ),
159 | fn(SqlError $err) => $onSuccess(
160 | ClosureResult::create(
161 | ClosureResult::STATE_FAILURE,
162 | $err->getMessage()
163 | )
164 | )
165 | );
166 | }
167 |
168 | /**
169 | * This function allows you to check if the arena exists.
170 | *
171 | * @param string $name
172 | * @param Closure $onSuccess : function(ClosureResult $result) : void{}
173 | * @return void
174 | */
175 | public static function isValidArena(string $name, Closure $onSuccess): void
176 | {
177 | Main::getInstaance()->getProvider()->db()->executeSelect(
178 | SQLKeyStorer::GET_ARENA,
179 | [
180 | "name" => $name
181 | ],
182 | fn(array $rows) => $onSuccess(
183 | ClosureResult::create(
184 | ClosureResult::STATE_SUCCESS,
185 | !empty($rows)
186 | )
187 | ),
188 | fn(SqlError $err) => $onSuccess(
189 | ClosureResult::create(
190 | ClosureResult::STATE_FAILURE,
191 | $err->getMessage()
192 | )
193 | )
194 | );
195 | }
196 | }
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/Main.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Closure;
41 | use Exception;
42 | use Generator;
43 | use Laith98Dev\BanCommands\Main as BanCommands;
44 | use Laith98Dev\FFA\commands\FFACommand;
45 | use Laith98Dev\FFA\game\Arena;
46 | use Laith98Dev\FFA\providers\DefaultProvider;
47 | use Laith98Dev\FFA\tasks\ArenasTask;
48 | use Laith98Dev\FFA\utils\ClosureResult;
49 | use Laith98Dev\FFA\utils\SQLKeyStorer;
50 |
51 | use pocketmine\data\bedrock\EnchantmentIdMap;
52 | use pocketmine\data\bedrock\EnchantmentIds;
53 |
54 | use pocketmine\plugin\PluginBase;
55 | use pocketmine\event\Listener;
56 |
57 | use pocketmine\event\entity\EntityDamageByEntityEvent;
58 | use pocketmine\event\entity\EntityDamageEvent;
59 | use pocketmine\event\entity\EntityTeleportEvent;
60 | use pocketmine\event\player\PlayerDropItemEvent;
61 | use pocketmine\event\player\PlayerQuitEvent;
62 | use pocketmine\event\player\PlayerExhaustEvent;
63 | use pocketmine\event\block\BlockBreakEvent;
64 | use pocketmine\event\block\BlockPlaceEvent;
65 |
66 | use pocketmine\item\enchantment\EnchantmentInstance;
67 | use pocketmine\item\enchantment\StringToEnchantmentParser;
68 | use pocketmine\item\LegacyStringToItemParser;
69 | use pocketmine\item\StringToItemParser;
70 | use pocketmine\player\Player;
71 |
72 | use pocketmine\utils\{Config, TextFormat as TF};
73 | use poggit\libasynql\SqlError;
74 | use SOFe\AwaitGenerator\Await;
75 |
76 | class Main extends PluginBase implements Listener
77 | {
78 | /** @var Arena[] */
79 | private array $arenas = [];
80 |
81 | private static self $instance;
82 |
83 | private DefaultProvider $provider;
84 |
85 | private BanCommands $banCommands;
86 |
87 | private array $defaultData = [
88 | "scoreboardIp" => "play.example.net",
89 | "banned-commands" => ["/kill"],
90 | "death-respawn-inMap" => true,
91 | "join-and-respawn-protected" => true,
92 | "protected-time" => 3,
93 | "protected-message" => "&eYou're now protected for &c{TIME} &eseconds",
94 | "death-attack-message" => "&e{PLAYER} &fwas killed by &c{KILLER}",
95 | "death-void-message" => "&c{PLAYER} &ffall into void",
96 | "respawn-message" => "&eRespawned",
97 | "join-message" => "&7{PLAYER} &ejoined to game.",
98 | "leave-message" => "&7{PLAYER} &ehas leave to game.",
99 | "kits" => [
100 | "default" => [
101 | "slot-0" => [
102 | "id" => "iron_sword",
103 | "count" => 1,
104 | "enchants" => []
105 | ],
106 | "slot-1" => [
107 | "id" => "golden_apple",
108 | "count" => 5,
109 | "enchants" => []
110 | ],
111 | "slot-2" => [
112 | "id" => "bow",
113 | "count" => 1,
114 | "enchants" => []
115 | ],
116 | "slot-3" => [
117 | "id" => "arrow",
118 | "count" => 15,
119 | "enchants" => []
120 | ],
121 | "helmet" => [
122 | "id" => "iron_helmet",
123 | "enchants" => []
124 | ],
125 | "chestplate" => [
126 | "id" => "iron_chestplate",
127 | "enchants" => [
128 | "id-" . EnchantmentIds::PROTECTION => [
129 | "level" => 2
130 | ]
131 | ]
132 | ],
133 | "leggings" => [
134 | "id" => "iron_leggings",
135 | "enchants" => []
136 | ],
137 | "boots" => [
138 | "id" => "iron_boots",
139 | "enchants" => []
140 | ]
141 | ]
142 | ],
143 | "kills-messages" => [
144 | "&eYou're the boss, you've got {KILLS} kills :).",
145 | "&eGood one, you've now reached {KILLS} kills :D.",
146 | "&eYou are now a great warrior, you've got {KILLS} kills ;D."
147 | ],
148 | "scoreboard-title" => "FFA",
149 | "provider" => "sqlite",
150 | "database" => [
151 | "type" => "sqlite",
152 | "sqlite" => [
153 | "file" => "arenas.sql"
154 | ],
155 | "mysql" => [
156 | "host" => "127.0.0.1",
157 | "username" => "root",
158 | "password" => "",
159 | "schema" => "your_schema"
160 | ],
161 | "worker-limit" => 1
162 | ]
163 | ];
164 |
165 | private array $kits = [];
166 |
167 | public static array $scoreboard_lines = [];
168 |
169 | public function onLoad(): void{
170 | self::$instance = $this;
171 | }
172 |
173 | public static function getInstaance(): Main{
174 | return self::$instance;
175 | }
176 |
177 | public function onEnable(): void{
178 | @mkdir($this->getDataFolder());
179 |
180 | $this->getServer()->getPluginManager()->registerEvents($this, $this);
181 |
182 | $this->getScheduler()->scheduleRepeatingTask(new ArenasTask($this), 20);
183 |
184 | $this->getServer()->getCommandMap()->register($this->getName(), new FFACommand($this));
185 |
186 | $this->banCommands = $this->getServer()->getPluginManager()->getPlugin("BanCommands");
187 |
188 | $this->initConfig();
189 | $this->setProvider();
190 | $this->loadKits();
191 | $this->loadArenas();
192 | $this->loadBannedCommands();
193 | $this->setScoreTitle();
194 | }
195 |
196 | /**
197 | * @return BanCommands
198 | */
199 | public function getBanManager(): BanCommands{
200 | return $this->banCommands;
201 | }
202 |
203 | /**
204 | * @return void
205 | */
206 | private function initConfig(){
207 | if(!is_file($this->getDataFolder() . "config.yml")){
208 | (new Config($this->getDataFolder() . "config.yml", Config::YAML, $this->defaultData));
209 | } else {
210 | $cfg = new Config($this->getDataFolder() . "config.yml", Config::YAML);
211 | $all = $cfg->getAll();
212 | foreach (array_keys($this->defaultData) as $key){
213 | if(!isset($all[$key])){
214 | rename($this->getDataFolder() . "config.yml", $this->getDataFolder() . "config_old.yml");
215 |
216 | (new Config($this->getDataFolder() . "config.yml", Config::YAML, $this->defaultData));
217 |
218 | break;
219 | }
220 | }
221 | }
222 | }
223 |
224 | private function setScoreTitle(){
225 | $index = [];
226 | $title = $this->getConfig()->get("scoreboard-title", "FFA");
227 |
228 | $index[] = TF::BOLD . TF::YELLOW . $title;
229 | $v = 0;
230 | for ($i = 0; $i < strlen($title); $i++){
231 | $final = "";
232 | for($i_ = 0; $i_ < strlen($title); $i_++){
233 | if($i_ == $v){
234 | $final .= TF::BOLD . TF::WHITE . $title[$i_];
235 | } else {
236 | $final .= TF::BOLD . TF::YELLOW . $title[$i_];
237 | }
238 | }
239 | $index[] = $final;
240 | $v++;
241 | }
242 |
243 | $index[] = TF::BOLD . TF::WHITE . $title;
244 | self::$scoreboard_lines = $index;
245 | }
246 |
247 | /**
248 | * @return void
249 | */
250 | private function setProvider(): void{
251 | $prov = $this->getConfig()->get("provider");
252 | $provider = match (strtolower($prov)){
253 | "sqlite" => new DefaultProvider($this),
254 | default => null
255 | };
256 |
257 | if($provider === null){
258 | $this->getLogger()->error("Invalid provider, expected 'sqlite', but got '" . strval($prov) . "'");
259 | $this->getServer()->getPluginManager()->disablePlugin($this);
260 | return;
261 | }
262 |
263 | $this->provider = $provider;
264 | }
265 |
266 | /**
267 | * @internal This function could change anytime without warning.
268 | * @return void
269 | */
270 | public function loadKits(): void{
271 | $cfg = new Config($this->getDataFolder() . "config.yml", Config::YAML, $this->defaultData);
272 | $kits = $cfg->get("kits", []);
273 |
274 | foreach ($kits as $name => $data){
275 | $items = [];
276 | $armors = [];
277 |
278 | foreach ($data as $slot_ => $slotData){
279 | if(str_starts_with($slot_, "slot-") !== false){
280 | $slot = str_replace("slot-", "", $slot_);
281 | foreach (["id", "count", "enchants"] as $key){
282 | if(!isset($slotData[$key])){
283 | $this->getLogger()->error("Failed to load default kit, Error: Missing a required key of slot #" . $slot . " (" . $key . ")");
284 | $this->getServer()->getPluginManager()->disablePlugin($this);
285 | continue;
286 | }
287 | }
288 |
289 | $id = $slotData["id"] ?? 0;
290 | $count = $slotData["count"] ?? 1;
291 | $enchants = $slotData["enchants"] ?? [];
292 |
293 | try{
294 | $item = LegacyStringToItemParser::getInstance()->parse($id) ?? StringToItemParser::getInstance()->parse($id);
295 | $item->setCount($count);
296 | } catch (Exception){
297 | $this->getLogger()->error("'" . $name . "' kit has an invalid item identifier: '" . $id . "'");
298 | continue;
299 | }
300 |
301 | if($item->isNull()){
302 | continue;
303 | }
304 |
305 | if(count($enchants) > 0){
306 | foreach ($enchants as $id_ => $enchantData){
307 | $eId = str_replace("id-", "", $id_);
308 | if(!isset($enchantData["level"])){
309 | $this->getLogger()->error("Failed to load '" . $name . "' kit, Error: Missing a required key of enchant for item " . $eId . " (level)");
310 | $this->getServer()->getPluginManager()->disablePlugin($this);
311 | continue;
312 | }
313 |
314 | $eLevel = intval($enchantData["level"]);
315 |
316 | try {
317 | $enchantment = EnchantmentIdMap::getInstance()->fromId(intval($eId)) ?? StringToEnchantmentParser::getInstance()->parse($eId);
318 | } catch (Exception){
319 | continue;
320 | }
321 |
322 | $item->addEnchantment(new EnchantmentInstance($enchantment, $eLevel));
323 | }
324 | }
325 |
326 | $items[$slot] = $item;
327 | continue;
328 | }
329 |
330 | if(in_array($slot_, ["helmet", "chestplate", "leggings", "boots"])){
331 | foreach (["id", "enchants"] as $key){
332 | if(!isset($slotData[$key])){
333 | $this->getLogger()->error("Failed to load '" . $name . "' kit, Error: Missing a required key of armor (" . $key . ")");
334 | $this->getServer()->getPluginManager()->disablePlugin($this);
335 | continue;
336 | }
337 | }
338 |
339 | $id = $slotData["id"];
340 | $enchants = $slotData["enchants"];
341 |
342 | try{
343 | $item = LegacyStringToItemParser::getInstance()->parse($id) ?? StringToItemParser::getInstance()->parse($id);
344 | } catch (Exception){
345 | $this->getLogger()->error("'" . $name . "' kit has an invalid item identifier: '" . $id . "'");
346 | continue;
347 | }
348 |
349 | if($item->isNull()){
350 | continue;
351 | }
352 |
353 | if(!empty($enchants)){
354 | foreach ($enchants as $id_ => $enchantData){
355 | $eId = str_replace("id-", "", $id_);
356 | if(!isset($enchantData["level"])){
357 | $this->getLogger()->error("Failed to load '" . $name . "' kit, Error: Missing a required key of enchant id " . $eId . " (level)");
358 | $this->getServer()->getPluginManager()->disablePlugin($this);
359 | continue;
360 | }
361 |
362 | $eLevel = intval($enchantData["level"]);
363 |
364 | try {
365 | $enchantment = EnchantmentIdMap::getInstance()->fromId(intval($eId)) ?? StringToEnchantmentParser::getInstance()->parse($eId);
366 | } catch (Exception){
367 | continue;
368 | }
369 |
370 | $item->addEnchantment(new EnchantmentInstance($enchantment, $eLevel));
371 | }
372 | }
373 |
374 | $armors[$slot_] = $item;
375 | }
376 | }
377 |
378 | $this->kits[$name]["items"] = $items;
379 | $this->kits[$name]["armors"] = $armors;
380 | }
381 | }
382 |
383 | /**
384 | * @return void
385 | */
386 | private function loadBannedCommands(): void{
387 | $cfg = new Config($this->getDataFolder() . "config.yml", Config::YAML);
388 | $commands = array_map("strtolower", $cfg->get("banned-commands", []));
389 | foreach ($commands as $cmd){
390 | if(!$this->getBanManager()->addCommand($cmd)){
391 | $this->getLogger()->info("Failed to ban the '" . $cmd . "' command; it's already banned.");
392 | }
393 | }
394 | }
395 |
396 | /**
397 | * @internal This function could change anytime without warning.
398 | * @return void
399 | */
400 | public function loadArenas(): void{
401 | if($this->isDisabled()) return;
402 |
403 | Await::f2c(function (): Generator{
404 | $rows = yield from Await::promise(
405 | fn(Closure $resolve) => $this->getProvider()->db()->executeSelect(
406 | SQLKeyStorer::GET_ARENAS,
407 | [],
408 | $resolve,
409 | fn(SqlError $err) => $this->getLogger()->error("Failed to load the arenas; error: " . $err->getMessage())
410 | )
411 | );
412 |
413 | if(!empty($rows)){
414 | foreach ($rows as $data){
415 | if(!isset($data["name"], $data["world"], $data["lobby"], $data["respawn"])){
416 | if(isset($data["name"])){
417 | $this->getLogger()->error("Failed to load arena '" . $data["name"] . "' because of corrupt data.");
418 | }
419 | continue;
420 | }
421 |
422 | $this->getServer()->getWorldManager()->loadWorld($data["world"]);
423 | if(($world = $this->getServer()->getWorldManager()->getWorldByName($data["world"])) === null){
424 | $this->getLogger()->error("There is an error with loading the world '" . $data["world"] . "' of the arena '" . $data["name"] . "'.");
425 | continue;
426 | }
427 |
428 | $world->setTime(1000);
429 | $world->stopTime();
430 |
431 | $data["lobby"] = json_decode($data["lobby"], true);
432 | $data["respawn"] = json_decode($data["respawn"], true);
433 |
434 | $this->arenas[$data["name"]] = new Arena($data);
435 | }
436 | }
437 | });
438 | }
439 |
440 | public function addArena(array $data, Closure $onSuccess){
441 | Await::f2c(function () use ($data, $onSuccess): Generator{
442 | if(!isset($data["name"], $data["world"], $data["lobby"], $data["respawn"])){
443 | $onSuccess(
444 | ClosureResult::create(
445 | ClosureResult::STATE_FAILURE,
446 | true
447 | )
448 | );
449 | return;
450 | }
451 |
452 | $name = $data["name"];
453 | $world = $data["world"];
454 | $lobby = $data["lobby"];
455 | $respawn = $data["respawn"];
456 |
457 | yield from Await::promise(
458 | fn(Closure $resolve) => $this->getProvider()->db()->executeInsert(
459 | SQLKeyStorer::ADD_ARENA,
460 | [
461 | "name" => $name,
462 | "world" => $world,
463 | "lobby" => json_encode($lobby),
464 | "respawn" => json_encode($respawn)
465 | ],
466 | $resolve,
467 | fn(SqlError $err) => $onSuccess(
468 | ClosureResult::create(
469 | ClosureResult::STATE_FAILURE,
470 | $err->getMessage()
471 | )
472 | )
473 | )
474 | );
475 |
476 | $onSuccess(
477 | ClosureResult::create(
478 | ClosureResult::STATE_SUCCESS,
479 | true
480 | )
481 | );
482 |
483 | $this->arenas[$data["name"]] = new Arena($data);
484 | });
485 | }
486 |
487 | /**
488 | * @param string $name
489 | * @param Closure $onSuccess
490 | * @return void
491 | */
492 | public function removeArena(string $name, Closure $onSuccess){
493 | Await::f2c(function () use ($name, $onSuccess): Generator{
494 | /**
495 | * @var ClosureResult $isValid
496 | */
497 | $isValid = yield from Await::promise(
498 | fn(Closure $resolve) => API::isValidArena($name, $resolve)
499 | );
500 |
501 | if($isValid->getValue()){
502 | if(($arena = $this->getArena($name)) !== null){
503 | foreach ($arena->getPlayers() as $player){
504 | $arena->quitPlayer($player);
505 | }
506 | }
507 |
508 | if(isset($this->arenas[$name])){
509 | unset($this->arenas[$name]);
510 | }
511 |
512 | $onSuccess(
513 | ClosureResult::create(
514 | ClosureResult::STATE_SUCCESS,
515 | true
516 | )
517 | );
518 | } else {
519 | $onSuccess(
520 | ClosureResult::create(
521 | ClosureResult::STATE_FAILURE,
522 | false
523 | )
524 | );
525 | }
526 | });
527 | }
528 |
529 | /**
530 | * @return array
531 | */
532 | public function getKits(): array{
533 | return $this->kits;
534 | }
535 |
536 | /**
537 | * @return Arena[]
538 | */
539 | public function getArenas(): array{
540 | return $this->arenas;
541 | }
542 |
543 | /**
544 | * @return DefaultProvider|null
545 | */
546 | public function getProvider(): ?DefaultProvider{
547 | return $this->provider;
548 | }
549 |
550 | /**
551 | * @param string $name
552 | * @return Arena|null
553 | */
554 | public function getArena(string $name): ?Arena{
555 | return isset($this->arenas[$name]) ? $this->arenas[$name] : null;
556 | }
557 |
558 | /**
559 | * @param Player $player
560 | * @param string $name
561 | * @return bool
562 | */
563 | public function joinArena(Player $player, string $name): bool{
564 | if(($arena = $this->getArena($name)) == null){
565 | $player->sendMessage(TF::RED . "Arena not found!");
566 | return false;
567 | }
568 |
569 | if($this->getPlayerArena($player) !== null){
570 | $player->sendMessage(TF::RED . "You're already in the arena!");
571 | return false;
572 | }
573 |
574 | if($arena->joinPlayer($player)){
575 | return true;
576 | }
577 |
578 | return false;
579 | }
580 |
581 | /**
582 | * @param Player $player
583 | * @return bool
584 | */
585 | public function joinRandomArena(Player $player): bool{
586 | if($this->getPlayerArena($player) !== null){
587 | $player->sendMessage(TF::RED . "You're already in the arena!");
588 | return false;
589 | }
590 |
591 | if(empty($this->getArenas())){
592 | $player->sendMessage(TF::RED . "No arenas were found!");
593 | return false;
594 | }
595 |
596 | $all = [];
597 | foreach ($this->getArenas() as $arena){
598 | $all[$arena->getName()] = count($arena->getPlayers());
599 | }
600 |
601 | arsort($all);
602 |
603 | foreach ($all as $arena_name => $players_count){
604 | if($this->joinArena($player, $arena_name)){
605 | return true;
606 | }
607 | }
608 |
609 | return false;
610 | }
611 |
612 | /**
613 | * @param Player $player
614 | * @return Arena|null
615 | */
616 | public function getPlayerArena(Player $player): ?Arena{
617 | foreach ($this->getArenas() as $arena){
618 | if($arena->inArena($player)){
619 | return $arena;
620 | }
621 | }
622 |
623 | return null;
624 | }
625 |
626 | /**
627 | * @param PlayerDropItemEvent $event
628 | * @return void
629 | */
630 | public function onDrop(PlayerDropItemEvent $event){
631 | if($this->getPlayerArena($event->getPlayer()) !== null){
632 | $event->cancel();
633 | }
634 | }
635 |
636 | /**
637 | * @param PlayerExhaustEvent $event
638 | * @return void
639 | */
640 | public function onHunger(PlayerExhaustEvent $event){
641 | if($this->getPlayerArena($event->getPlayer()) !== null){
642 | $event->cancel();
643 | }
644 | }
645 |
646 | /**
647 | * @param PlayerQuitEvent $event
648 | * @return void
649 | */
650 | public function onQuit(PlayerQuitEvent $event){
651 | if(($arena = $this->getPlayerArena($event->getPlayer())) !== null){
652 | $arena->quitPlayer($event->getPlayer());
653 | }
654 | }
655 |
656 | /**
657 | * @param EntityTeleportEvent $event
658 | * @return void
659 | */
660 | public function onLevelChange(EntityTeleportEvent $event){
661 | $player = $event->getEntity();
662 | $from = $event->getFrom();
663 | $to = $event->getTo();
664 | if($player instanceof Player){
665 | if(($arena = $this->getPlayerArena($player)) !== null && $from->getWorld()->getFolderName() !== $to->getWorld()->getFolderName()){
666 | $arena->quitPlayer($player);
667 | }
668 | }
669 | }
670 |
671 | /**
672 | * @param BlockPlaceEvent $event
673 | * @return void
674 | */
675 | public function onPlace(BlockPlaceEvent $event){
676 | $player = $event->getPlayer();
677 | if($this->getPlayerArena($player) !== null){
678 | if(!$player->isCreative()){
679 | $event->cancel();
680 | }
681 | }
682 | }
683 |
684 | /**
685 | * @param BlockBreakEvent $event
686 | * @return void
687 | */
688 | public function onBreak(BlockBreakEvent $event){
689 | $player = $event->getPlayer();
690 | if($this->getPlayerArena($player) !== null){
691 | if(!$player->isCreative()){
692 | $event->cancel();
693 | }
694 | }
695 | }
696 |
697 | /**
698 | * @param EntityDamageEvent $event
699 | * @return void
700 | */
701 | public function onDamage(EntityDamageEvent $event): void{
702 | $entity = $event->getEntity();
703 | if($entity instanceof Player){
704 | if(($arena = $this->getPlayerArena($entity)) !== null){
705 | if($event->getCause() == EntityDamageEvent::CAUSE_FALL){
706 | $event->cancel();
707 | return;
708 | }
709 |
710 | if($entity->getHealth() <= $event->getFinalDamage()){
711 | $arena->killPlayer($entity);
712 | $event->cancel();
713 | }
714 | }
715 | }
716 | }
717 |
718 | /**
719 | * @param EntityDamageByEntityEvent $event
720 | * @return void
721 | */
722 | public function onDamageByEntity(EntityDamageByEntityEvent $event): void
723 | {
724 | $entity = $event->getEntity();
725 | $damager = $event->getDamager();
726 |
727 | if($entity instanceof Player && $damager instanceof Player){
728 | if(($arena = $this->getPlayerArena($entity)) !== null){
729 | if($arena->isProtected($entity) || $arena->isProtected($damager)){
730 | $event->cancel();
731 | }
732 | }
733 | }
734 | }
735 | }
736 |
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/commands/FFACommand.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Closure;
41 | use Generator;
42 | use Laith98Dev\FFA\API;
43 | use Laith98Dev\FFA\Main;
44 | use Laith98Dev\FFA\utils\ClosureResult;
45 | use Laith98Dev\FFA\utils\SQLKeyStorer;
46 |
47 | use pocketmine\command\CommandSender;
48 | use pocketmine\command\Command;
49 | use pocketmine\plugin\PluginOwned;
50 |
51 | use pocketmine\utils\TextFormat as TF;
52 |
53 | use pocketmine\player\Player;
54 | use SOFe\AwaitGenerator\Await;
55 |
56 | class FFACommand extends Command implements PluginOwned
57 | {
58 | public function __construct(
59 | private Main $plugin
60 | ){
61 | parent::__construct("ffa", "FFA Commands", null, ["freeforall"]);
62 | $this->setPermission("ffa.command");
63 | }
64 |
65 | public function getOwningPlugin() : Main{
66 | return $this->plugin;
67 | }
68 |
69 | public function execute(CommandSender $sender, string $cmdLabel, array $args): bool{
70 | if(!($sender instanceof Player)){
71 | $sender->sendMessage("run command in-game only");
72 | return false;
73 | }
74 |
75 | if(!isset($args[0])){
76 | $sender->sendMessage(TF::RED . "Usage: /" . $cmdLabel . " help");
77 | return false;
78 | }
79 |
80 | switch ($args[0]){
81 | case "help":
82 | $sender->sendMessage(TF::YELLOW . "========================");
83 | if($sender->hasPermission("ffa.command.admin")){
84 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " help");
85 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " create");
86 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " remove");
87 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " setlobby");
88 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " setrespawn");
89 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " reload");
90 | $sender->sendMessage (TF::GREEN . "- /" . $cmdLabel . " list");
91 | }
92 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " join");
93 | $sender->sendMessage(TF::GREEN . "- /" . $cmdLabel . " quit");
94 | $sender->sendMessage(TF::YELLOW . "========================");
95 | break;
96 |
97 | case "create":
98 | if(!$sender->hasPermission("ffa.command.admin")){
99 | return false;
100 | }
101 | if(!isset($args[1])){
102 | $sender->sendMessage(TF::RED . "Usage: /" . $cmdLabel . " create ");
103 | return false;
104 | }
105 |
106 | $arenaName = $args[1];
107 | $world = $sender->getWorld();
108 |
109 | if($world->getFolderName() == $this->plugin->getServer()->getWorldManager()->getDefaultWorld()->getFolderName()){
110 | $sender->sendMessage(TF::RED . "You cannot create a game in the default world!");
111 | return false;
112 | }
113 |
114 | Await::f2c(function () use($sender, $arenaName, $world): Generator{
115 |
116 | /**
117 | * @var ClosureResult $isValid
118 | */
119 | $isValid = yield from Await::promise(
120 | fn(Closure $resolve) => API::isValidArena($arenaName, $resolve)
121 | );
122 |
123 | if($isValid->getValue()){
124 | $sender->sendMessage(TF::RED . "There is an arena with this name.");
125 | } else {
126 | $data = [
127 | "name" => $arenaName,
128 | "world" => $world->getFolderName(),
129 | "lobby" => [],
130 | "respawn" => []
131 | ];
132 |
133 | /**
134 | * @var ClosureResult $response
135 | */
136 | $response = yield from Await::promise(
137 | fn(Closure $resolve) => $this->getOwningPlugin()->addArena(
138 | $data,
139 | $resolve
140 | )
141 | );
142 |
143 | if($response->getValue()){
144 | $sender->sendMessage(TF::YELLOW . "Arena was created successfully.");
145 | } else {
146 | $sender->sendMessage(TF::RED . "There was an error while trying to add this arena; please check your console to see the error and contact the developer!");
147 | }
148 | }
149 | });
150 | break;
151 |
152 | case "remove":
153 | if(!$sender->hasPermission("ffa.command.admin")){
154 | return false;
155 | }
156 |
157 | if(!isset($args[1])){
158 | $sender->sendMessage(TF::RED . "Usage: /" . $cmdLabel . " remove ");
159 | return false;
160 | }
161 |
162 | $arenaName = $args[1];
163 |
164 | Await::f2c(function () use($sender, $arenaName): Generator{
165 | /**
166 | * @var ClosureResult $response
167 | */
168 | $response = yield from Await::promise(
169 | fn(Closure $resolve) => $this->getOwningPlugin()->removeArena(
170 | $arenaName,
171 | $resolve
172 | )
173 | );
174 |
175 | if($response->getValue()){
176 | $sender->sendMessage(TF::GREEN . "Arena was deleted successfully!");
177 | } else {
178 | $sender->sendMessage(TF::RED . "Arena does not exist");
179 | }
180 | });
181 |
182 | break;
183 |
184 | case "setlobby":
185 | if(!$sender->hasPermission("ffa.command.admin")){
186 | return false;
187 | }
188 |
189 | Await::f2c(function () use ($sender, $cmdLabel): Generator{
190 | $world = $sender->getWorld();
191 | $arena = null;
192 | $arenaName = null;
193 |
194 | $rows = yield from Await::promise(
195 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeSelect(
196 | SQLKeyStorer::GET_ARENAS,
197 | [],
198 | $resolve,
199 | )
200 | );
201 |
202 | if(!empty($rows)){
203 | foreach ($rows as $arena){
204 | if(strtolower($arena["world"]) == strtolower($world->getFolderName())){
205 | $arenaName = $arena["name"];
206 | }
207 | }
208 |
209 | if($arenaName === null){
210 | $sender->sendMessage(TF::RED . "Arena does not exist; try creating it using the command /" . $cmdLabel . " create!");
211 | return false;
212 | }
213 |
214 | $data = [
215 | "PX" => $sender->getLocation()->x,
216 | "PY" => $sender->getLocation()->y,
217 | "PZ" => $sender->getLocation()->z,
218 | "YAW" => $sender->getLocation()->yaw,
219 | "PITCH" => $sender->getLocation()->pitch
220 | ];
221 |
222 | yield from Await::promise(
223 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeChange(
224 | SQLKeyStorer::UPDATE_LOBBY,
225 | [
226 | "name" => $arenaName,
227 | "lobby" => json_encode($data)
228 | ],
229 | $resolve
230 | )
231 | );
232 |
233 | $arenas_rows = yield from Await::promise(
234 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeSelect(
235 | SQLKeyStorer::GET_ARENAS,
236 | [],
237 | $resolve
238 | )
239 | );
240 |
241 | if(!empty($arenas_rows)){
242 | foreach ($arenas_rows as $data){
243 | if(strtolower($data["name"]) == strtolower($arenaName)){
244 | $data["lobby"] = json_decode($data["lobby"], true);
245 | $data["respawn"] = json_decode($data["respawn"], true);
246 |
247 | if(($arena = $this->getOwningPlugin()->getArena($arenaName)) !== null){
248 | $arena->updateData($data);
249 | }
250 |
251 | $sender->sendMessage(TF::YELLOW . "successfully updated lobby position for '" . $arenaName . "'!");
252 | break;
253 | }
254 | }
255 | }
256 | }
257 | });
258 |
259 | break;
260 |
261 | case "setrespawn":
262 | if(!$sender->hasPermission("ffa.command.admin")){
263 | return false;
264 | }
265 |
266 | Await::f2c(function () use ($sender, $cmdLabel): Generator{
267 | $world = $sender->getWorld();
268 | $arena = null;
269 | $arenaName = null;
270 |
271 | $rows = yield from Await::promise(
272 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeSelect(
273 | SQLKeyStorer::GET_ARENAS,
274 | [],
275 | $resolve,
276 | )
277 | );
278 |
279 | if(!empty($rows)){
280 | foreach ($rows as $arena){
281 | if(strtolower($arena["world"]) == strtolower($world->getFolderName())){
282 | $arenaName = $arena["name"];
283 | }
284 | }
285 |
286 | if($arenaName === null){
287 | $sender->sendMessage(TF::RED . "Arena does not exist; try creating it using the command /" . $cmdLabel . " create!");
288 | return false;
289 | }
290 |
291 | $data = [
292 | "PX" => $sender->getLocation()->x,
293 | "PY" => $sender->getLocation()->y,
294 | "PZ" => $sender->getLocation()->z,
295 | "YAW" => $sender->getLocation()->yaw,
296 | "PITCH" => $sender->getLocation()->pitch
297 | ];
298 |
299 | yield from Await::promise(
300 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeChange(
301 | SQLKeyStorer::UPDATE_RESPAWN,
302 | [
303 | "name" => $arenaName,
304 | "respawn" => json_encode($data)
305 | ],
306 | $resolve
307 | )
308 | );
309 |
310 | $arenas_rows = yield from Await::promise(
311 | fn(Closure $resolve) => $this->getOwningPlugin()->getProvider()->db()->executeSelect(
312 | SQLKeyStorer::GET_ARENAS,
313 | [],
314 | $resolve
315 | )
316 | );
317 |
318 | if(!empty($arenas_rows)){
319 | foreach ($arenas_rows as $data){
320 | if(strtolower($data["name"]) == strtolower($arenaName)){
321 | $data["lobby"] = json_decode($data["lobby"], true);
322 | $data["respawn"] = json_decode($data["respawn"], true);
323 |
324 | if(($arena = $this->getOwningPlugin()->getArena($arenaName)) !== null){
325 | $arena->updateData($data);
326 | }
327 |
328 | $sender->sendMessage(TF::YELLOW . "successfully updated respawn position for '" . $arenaName . "'!");
329 | break;
330 | }
331 | }
332 | }
333 | }
334 | });
335 |
336 | break;
337 |
338 | case "list":
339 | if(!$sender->hasPermission("ffa.command.admin")){
340 | return false;
341 | }
342 |
343 | $sender->sendMessage(TF::GREEN . "Arenas:");
344 | foreach ($this->plugin->getArenas() as $arena){
345 | $sender->sendMessage(TF::YELLOW . "- " . $arena->getName() . " => Players: " . count($arena->getPlayers()));
346 | }
347 | break;
348 |
349 | case "join":
350 | if(isset($args[1])){
351 | $player = $sender;
352 |
353 | if(isset($args[2]) && $sender->hasPermission("ffa.command.admin")){
354 | if(($pp = $this->getOwningPlugin()->getServer()->getPlayerByPrefix($args[2])) !== null){
355 | $player = $pp;
356 | }
357 | }
358 |
359 | if($this->getOwningPlugin()->joinArena($player, $args[1])){
360 | return true;
361 | }
362 | } else {
363 | if($this->getOwningPlugin()->joinRandomArena($sender)){
364 | return true;
365 | }
366 | }
367 | break;
368 |
369 | case "quit":
370 | if(($arena = $this->getOwningPlugin()->getPlayerArena($sender)) !== null){
371 | if($arena->quitPlayer($sender)){
372 | return true;
373 | }
374 | } else {
375 | $sender->sendMessage("You're not in an arena!");
376 | return false;
377 | }
378 | break;
379 |
380 | case "reload":
381 | if(!$sender->hasPermission("ffa.command.admin")){
382 | return false;
383 | }
384 |
385 | foreach ($this->getOwningPlugin()->getArenas() as $arena){
386 | foreach ($arena->getPlayers() as $player){
387 | $arena->quitPlayer($player);
388 | }
389 | }
390 |
391 | $this->getOwningPlugin()->loadKits();
392 | $this->getOwningPlugin()->loadArenas();
393 |
394 | $sender->sendMessage(TF::GREEN . "You've reloaded the plugin successfully!");
395 | break;
396 |
397 | default:
398 | $sender->sendMessage(TF::RED . "Usage: /" . $cmdLabel . " help");
399 | break;
400 | }
401 |
402 | return false;
403 | }
404 | }
405 |
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/game/Arena.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Closure;
41 | use Generator;
42 | use Laith98Dev\FFA\API;
43 | use Laith98Dev\FFA\Main;
44 | use Laith98Dev\FFA\utils\ClosureResult;
45 | use Laith98Dev\FFA\utils\Utils;
46 |
47 | use pocketmine\player\Player;
48 | use pocketmine\player\GameMode;
49 |
50 | use pocketmine\world\Position;
51 |
52 | use pocketmine\utils\TextFormat as TF;
53 |
54 | use pocketmine\event\entity\EntityDamageEvent;
55 | use pocketmine\event\entity\EntityDamageByEntityEvent;
56 |
57 | use pocketmine\network\mcpe\protocol\RemoveObjectivePacket;
58 | use pocketmine\network\mcpe\protocol\SetDisplayObjectivePacket;
59 | use pocketmine\network\mcpe\protocol\SetScorePacket;
60 | use pocketmine\network\mcpe\protocol\types\ScorePacketEntry;
61 | use pocketmine\world\World;
62 | use SOFe\AwaitGenerator\Await;
63 |
64 | class Arena
65 | {
66 | private array $players = [];
67 |
68 | private array $scoreboards = [];
69 |
70 | private int $scoreboardsLine = 0;
71 |
72 | private array $protect = [];
73 |
74 | public function __construct(
75 | private array $data
76 | ){
77 | // NOOP
78 | }
79 |
80 | public function getPlugin(): Main{
81 | return Main::getInstaance();
82 | }
83 |
84 | public function updateData(array $data): void{
85 | $this->data = $data;
86 | }
87 |
88 | public function getData(): array{
89 | return $this->data;
90 | }
91 |
92 | public function getName(): string{
93 | return $this->getData()["name"];
94 | }
95 |
96 | public function getWorldName(): string{
97 | return $this->getData()["world"];
98 | }
99 |
100 | public function getLobby(): array{
101 | return $this->getData()["lobby"];
102 | }
103 |
104 | public function getRespawn(): array{
105 | return $this->getData()["respawn"];
106 | }
107 |
108 | /**
109 | * @return Player[]
110 | */
111 | public function getPlayers(): array{
112 | return $this->players;
113 | }
114 |
115 | /**
116 | * @param Player $player
117 | * @return bool
118 | */
119 | public function isProtected(Player $player): bool{
120 | return isset($this->protect[$player->getName()]);
121 | }
122 |
123 | public function getProtectTime(Player $player): int{
124 | return $this->protect[$player->getName()] ?? 0;
125 | }
126 |
127 | public function inArena(Player $player): bool{
128 | return isset($this->players[$player->getName()]) ? true : false;
129 | }
130 |
131 | public function new(Player $player, string $objectiveName, string $displayName): void{
132 | if(isset($this->scoreboards[$player->getName()])){
133 | $this->remove($player);
134 | }
135 |
136 | if($player->isConnected()) $player->getNetworkSession()->sendDataPacket(
137 | SetDisplayObjectivePacket::create(
138 | SetDisplayObjectivePacket::DISPLAY_SLOT_SIDEBAR,
139 | $objectiveName,
140 | $displayName,
141 | "dummy",
142 | SetDisplayObjectivePacket::SORT_ORDER_ASCENDING
143 | )
144 | );
145 | $this->scoreboards[$player->getName()] = $objectiveName;
146 | }
147 |
148 | public function remove(Player $player): void{
149 | $objectiveName = $this->getObjectiveName($player) ?? "ffa";
150 | if($player->isConnected()) $player->getNetworkSession()->sendDataPacket(
151 | RemoveObjectivePacket::create($objectiveName)
152 | );
153 | unset($this->scoreboards[$player->getName()]);
154 | }
155 |
156 | public function setLine(Player $player, int $score, string $message): void{
157 | if(!isset($this->scoreboards[$player->getName()])){
158 | $this->getPlugin()->getLogger()->error("You cannot set a score for a player with no scoreboard.");
159 | return;
160 | }
161 |
162 | if($score > 15 || $score < 1){
163 | $this->getPlugin()->getLogger()->error("Score must be between the value of 1-15. $score out of range");
164 | return;
165 | }
166 |
167 | $objectiveName = $this->getObjectiveName($player) ?? "ffa";
168 |
169 | $entry = new ScorePacketEntry();
170 | $entry->objectiveName = $objectiveName;
171 | $entry->type = $entry::TYPE_FAKE_PLAYER;
172 | $entry->customName = $message;
173 | $entry->score = $score;
174 | $entry->scoreboardId = $score;
175 |
176 | if($player->isConnected()) $player->getNetworkSession()->sendDataPacket(SetScorePacket::create(
177 | SetScorePacket::TYPE_CHANGE,
178 | [$entry]
179 | ));
180 | }
181 |
182 | public function getObjectiveName(Player $player): ?string{
183 | return isset($this->scoreboards[$player->getName()]) ? $this->scoreboards[$player->getName()] : null;
184 | }
185 |
186 | public function getWorld(?string $name = null): ?World{
187 | if($name === null){
188 | $this->getPlugin()->getServer()->getWorldManager()->loadWorld($this->getWorldName());
189 | return $this->getPlugin()->getServer()->getWorldManager()->getWorldByName($this->getWorldName());
190 | }
191 | return $this->getPlugin()->getServer()->getWorldManager()->getWorldByName($name);
192 | }
193 |
194 | public function broadcastMessage(string $message){
195 | foreach ($this->getPlayers() as $player){
196 | if($player->isConnected()) $player->sendMessage($message);
197 | }
198 | }
199 |
200 | public function joinPlayer(Player $player): bool{
201 | if(isset($this->players[$player->getName()])){
202 | return false;
203 | }
204 |
205 | $lobby = $this->getLobby();
206 |
207 | if(empty($lobby)){
208 | if($player->hasPermission("ffa.command.admin")){
209 | $player->sendMessage(TF::RED . "Please set lobby position. Usage: /ffa setlobby");
210 | }
211 | return false;
212 | }
213 |
214 | if(empty($this->getRespawn())){
215 | if($player->hasPermission("ffa.command.admin")){
216 | $player->sendMessage(TF::RED . "Please set respawn position. Usage: /ffa setrespawn");
217 | }
218 | return false;
219 | }
220 |
221 | $x = floatval($lobby["PX"]);
222 | $y = floatval($lobby["PY"]);
223 | $z = floatval($lobby["PZ"]);
224 | $yaw = floatval($lobby["YAW"]);
225 | $pitch = floatval($lobby["PITCH"]);
226 |
227 | $player->teleport(new Position($x, $y, $z, $this->getWorld()), $yaw, $pitch);
228 |
229 | $player->setGamemode(GameMode::ADVENTURE());
230 | $this->addItems($player);
231 |
232 | $this->players[$player->getName()] = $player;
233 |
234 | $this->broadcastMessage(Utils::messageFormat($this->getPlugin()->getConfig()->get("join-message"), $player, $this));
235 |
236 | if($this->getPlugin()->getConfig()->get("join-and-respawn-protected") === true){
237 | $this->protect[$player->getName()] = $this->getPlugin()->getConfig()->get("protected-time", 3);
238 | $player->sendMessage(Utils::messageFormat($this->getPlugin()->getConfig()->get("protected-message"), $player, $this));
239 | }
240 |
241 | return true;
242 | }
243 |
244 | public function quitPlayer(Player $player): bool{
245 |
246 | if(!isset($this->players[$player->getName()])){
247 | return false;
248 | }
249 |
250 | unset($this->players[$player->getName()]);
251 |
252 | $this->remove($player);
253 |
254 | $player->teleport($this->getPlugin()->getServer()->getWorldManager()->getDefaultWorld()->getSafeSpawn());
255 | $player->getInventory()->clearAll();
256 | $player->getArmorInventory()->clearAll();
257 | $player->getOffHandInventory()->clearAll();
258 | $player->getCraftingGrid()->clearAll();
259 | $player->getEffects()->clear();
260 | $player->setGamemode($this->getPlugin()->getServer()->getGamemode());
261 | $player->setHealth($player->getMaxHealth());
262 | $player->getHungerManager()->setFood($player->getHungerManager()->getMaxFood());
263 |
264 | $this->broadcastMessage(Utils::messageFormat($this->getPlugin()->getConfig()->get("leave-message"), $player, $this));
265 | return true;
266 | }
267 |
268 | public function killPlayer(Player $player): void{
269 | $message = null;
270 | $event = $player->getLastDamageCause();
271 |
272 | if($event === null){
273 | return;
274 | }
275 |
276 | $player->getInventory()->clearAll();
277 | $player->getArmorInventory()->clearAll();
278 | $player->getOffHandInventory()->clearAll();
279 | $player->getCraftingGrid()->clearAll();
280 | $player->getEffects()->clear();
281 |
282 | $player->setGamemode(GameMode::ADVENTURE());
283 | $player->setHealth($player->getMaxHealth());
284 | $player->getHungerManager()->setFood($player->getHungerManager()->getMaxFood());
285 |
286 | Await::f2c(function () use ($player, $event, $message): Generator{
287 | yield from Await::promise(
288 | fn(Closure $resolve) => API::addDeath($player, 1, $resolve)
289 | );
290 |
291 | switch ($event->getCause()){
292 | case EntityDamageEvent::CAUSE_ENTITY_ATTACK:
293 | $damager = $event instanceof EntityDamageByEntityEvent ? $event->getDamager() : null;
294 | if($damager !== null && $damager instanceof Player){
295 | $message = str_replace(["{PLAYER}", "{KILLER}", "&"], [$player->getName(), $damager->getName(), TF::ESCAPE], $this->getPlugin()->getConfig()->get("death-attack-message"));
296 |
297 | yield from Await::promise(
298 | fn(Closure $resolve) => API::addKill($damager, 1, $resolve)
299 | );
300 |
301 | $kills = yield from Await::promise(
302 | fn(Closure $resolve) => API::getKills($damager, fn(ClosureResult $response) => $resolve($response->getValue()))
303 | );
304 |
305 | if($kills % 5 === 0){
306 | $messages = $this->getPlugin()->getConfig()->get("kills-messages", []);
307 | if(!empty($messages)){
308 | $killMsg = $messages[array_rand($messages)];
309 | $killMsg = Utils::messageFormat($killMsg, $damager, $this);
310 | $killMsg = str_replace("{KILLS}", strval($kills), $killMsg);
311 | $damager->sendMessage($killMsg);
312 | }
313 | }
314 |
315 | $damager->setHealth($damager->getMaxHealth());
316 | $damager->sendPopup(TF::YELLOW . "+1 Kill");
317 | $this->addItems($damager);
318 | }
319 | break;
320 |
321 | case EntityDamageEvent::CAUSE_VOID:
322 | $message = str_replace(["{PLAYER}", "&"], [$player->getName(), TF::ESCAPE], $this->getPlugin()->getConfig()->get("death-void-message"));
323 | break;
324 | }
325 |
326 | if($message !== null){
327 | $this->broadcastMessage($message);
328 | }
329 |
330 | if($this->getPlugin()->getConfig()->get("death-respawn-inMap") === true){
331 | $this->respawn($player);
332 | } else {
333 | $this->quitPlayer($player);
334 | }
335 | });
336 | }
337 |
338 | public function respawn(Player $player){
339 | $player->setGamemode(GameMode::ADVENTURE());
340 | $player->setHealth($player->getMaxHealth());
341 | $player->getHungerManager()->setFood($player->getHungerManager()->getMaxFood());
342 |
343 | $this->addItems($player);
344 |
345 | $respawn = $this->getRespawn();
346 | $x = floatval($respawn["PX"]);
347 | $y = floatval($respawn["PY"]);
348 | $z = floatval($respawn["PZ"]);
349 | $yaw = floatval($respawn["YAW"]);
350 | $pitch = floatval($respawn["PITCH"]);
351 |
352 | $player->teleport(new Position($x, $y, $z, $this->getWorld()), $yaw, $pitch);
353 |
354 | if($this->getPlugin()->getConfig()->get("join-and-respawn-protected") === true){
355 | $this->protect[$player->getName()] = $this->getPlugin()->getConfig()->get("protected-time", 3);
356 | $player->sendMessage(str_replace(["{PLAYER}", "{TIME}", "&"], [$player->getName(), strval($this->protect[$player->getName()]), TF::ESCAPE], $this->getPlugin()->getConfig()->get("protected-message")));
357 | }
358 |
359 | $player->sendTitle(Utils::messageFormat($this->getPlugin()->getConfig()->get("respawn-message"), $player, $this));
360 | }
361 |
362 | private function addItems(Player $player){
363 | $player->getInventory()->clearAll();
364 | $player->getArmorInventory()->clearAll();
365 | $player->getOffHandInventory()->clearAll();
366 | $player->getCraftingGrid()->clearAll();
367 | $player->getEffects()->clear();
368 |
369 | $defaultKit = $this->getPlugin()->getKits()["default"];
370 |
371 | $items = $defaultKit["items"];
372 | $armors = $defaultKit["armors"];
373 |
374 | foreach ($items as $slot => $item){
375 | $player->getInventory()->setItem(intval($slot), $item);
376 | }
377 |
378 | foreach ($armors as $type => $item){
379 | switch ($type){
380 | case "helmet":
381 | $player->getArmorInventory()->setHelmet($item);
382 | break;
383 | case "chestplate":
384 | $player->getArmorInventory()->setChestplate($item);
385 | break;
386 | case "leggings":
387 | $player->getArmorInventory()->setLeggings($item);
388 | break;
389 | case "boots":
390 | $player->getArmorInventory()->setBoots($item);
391 | break;
392 | }
393 | }
394 | }
395 |
396 | public function tick(){
397 | foreach ($this->getPlayers() as $player){
398 | if(!$player->isConnected()) continue;
399 |
400 | API::getKills($player, function (ClosureResult $response) use ($player){
401 | $kills = $response->getValue();
402 | API::getDeaths($player, function (ClosureResult $response) use ($player, $kills){
403 | $deaths = $response->getValue();
404 |
405 | $this->new($player, "ffa", Main::$scoreboard_lines[$this->scoreboardsLine]);
406 | $this->setLine($player, 1, " ");
407 | $this->setLine($player, 2, " Players: " . TF::YELLOW . count($this->getPlayers()) . " ");
408 | $this->setLine($player, 3, " ");
409 | $this->setLine($player, 4, " Map: " . TF::YELLOW . $this->getName() . " ");
410 | $this->setLine($player, 5, " ");
411 | $this->setLine($player, 6, " Kills: " . TF::YELLOW . $kills . " ");
412 | $this->setLine($player, 7, " Deaths: " . TF::YELLOW . $deaths . " ");
413 | $this->setLine($player, 8, " ");
414 | $this->setLine($player, 9, " " . str_replace("&", TF::ESCAPE, $this->getPlugin()->getConfig()->get("scoreboardIp", "play.example.net") . " "));
415 | });
416 | });
417 | }
418 |
419 | if($this->scoreboardsLine == (count(Main::$scoreboard_lines) - 1)){
420 | $this->scoreboardsLine = 0;
421 | } else {
422 | ++$this->scoreboardsLine;
423 | }
424 |
425 | foreach ($this->protect as $name => $time){
426 | if($time == 0){
427 | unset($this->protect[$name]);
428 | } else {
429 | $this->protect[$name]--;
430 | }
431 | }
432 | }
433 | }
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/providers/DefaultProvider.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Laith98Dev\FFA\Main;
41 | use Laith98Dev\FFA\utils\SQLKeyStorer;
42 |
43 | use poggit\libasynql\libasynql;
44 | use poggit\libasynql\SqlError;
45 | use poggit\libasynql\DataConnector;
46 |
47 | class DefaultProvider {
48 |
49 | private DataConnector $db;
50 |
51 | public function __construct(
52 | private Main $plugin
53 | ){
54 | $this->init();
55 | }
56 |
57 | public function init(){
58 | if ($this->plugin->isDisabled()) return;
59 |
60 | try {
61 | $this->db = $db = libasynql::create(
62 | $this->plugin,
63 | $this->plugin->getConfig()->get("database"),
64 | ["sqlite" => "sqlite.sql"]
65 | );
66 | } catch (\Error $e) {
67 | $this->plugin->getLogger()->error($e->getMessage());
68 | return;
69 | }
70 |
71 | $error = null;
72 | $db->executeGeneric(
73 | SQLKeyStorer::INIT,
74 | [],
75 | null,
76 | function (SqlError $error_) use (&$error) {
77 | $error = $error_;
78 | }
79 | );
80 |
81 | $db->waitAll();
82 |
83 | if ($error !== null) {
84 | $this->plugin->getLogger()->error($error->getMessage());
85 | }
86 | }
87 |
88 | public function db(){
89 | return $this->db;
90 | }
91 |
92 | }
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/tasks/ArenasTask.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use pocketmine\scheduler\Task;
41 |
42 | use Laith98Dev\FFA\Main;
43 |
44 | class ArenasTask extends Task {
45 |
46 | public function __construct(
47 | private Main $plugin
48 | ){
49 | // NOOP
50 | }
51 |
52 | public function onRun(): void{
53 | foreach ($this->plugin->getArenas() as $arena){
54 | $arena->tick();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/utils/ClosureResult.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | class ClosureResult
41 | {
42 | public const STATE_FAILURE = 0;
43 | public const STATE_SUCCESS = 1;
44 |
45 | public function __construct(
46 | private int $status,
47 | private mixed $value = null
48 | ){
49 | // NOOP
50 | }
51 |
52 | public function getStatus(): int
53 | {
54 | return $this->status;
55 | }
56 |
57 | public function getValue(): mixed
58 | {
59 | return $this->value;
60 | }
61 |
62 | public static function create(int $status, mixed $value = null): self
63 | {
64 | return new self($status, $value);
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/utils/SQLKeyStorer.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | class SQLKeyStorer {
41 |
42 | public const INIT = "ffa.init";
43 | public const ADD_ARENA = "ffa.add-arena";
44 | public const DELETE_ARENA = "ffa.delete-arena";
45 | public const GET_ARENA = "ffa.get-arena";
46 | public const GET_ARENAS = "ffa.get-arenas";
47 |
48 | public const ADD_KILLS = "ffa.add-kills";
49 | public const ADD_DEATHS = "ffa.add-deaths";
50 | public const GET_KILLS = "ffa.get-kills";
51 | public const GET_DEATHS = "ffa.get-deaths";
52 | public const UPDATE_LOBBY = "ffa.update-lobby";
53 | public const UPDATE_RESPAWN = "ffa.update-respawn";
54 |
55 | }
--------------------------------------------------------------------------------
/src/Laith98Dev/FFA/utils/Utils.php:
--------------------------------------------------------------------------------
1 | _ <| | | |/ _ \ \ / /
14 | * | |___| (_| | | |_| | | |/ /| (_) | |__| | __/\ V /
15 | * |______\__,_|_|\__|_| |_/_/ \___/|_____/ \___| \_/
16 | *
17 | * Copyright (C) 2024 Laith98Dev
18 | *
19 | * Youtube: Laith Youtuber
20 | * Discord: Laith98Dev#0695 or @u.oo
21 | * Github: Laith98Dev
22 | * Email: spt.laithdev@gamil.com
23 | * Donate: https://paypal.me/Laith113
24 | *
25 | * This program is free software: you can redistribute it and/or modify
26 | * it under the terms of the GNU General Public License as published by
27 | * the Free Software Foundation, either version 3 of the License, or
28 | * (at your option) any later version.
29 | *
30 | * This program is distributed in the hope that it will be useful,
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 | * GNU General Public License for more details.
34 | *
35 | * You should have received a copy of the GNU General Public License
36 | * along with this program. If not, see .
37 | *
38 | */
39 |
40 | use Laith98Dev\FFA\game\Arena;
41 | use pocketmine\player\Player;
42 | use pocketmine\utils\TextFormat;
43 |
44 | class Utils {
45 |
46 | public static function messageFormat(string $msg, Player $player, Arena $game){
47 | $index = [
48 | "{PLAYER}" => $player->getName(),
49 | "{ARENA}" => $game->getName(),
50 | "{GAME}" => $game->getName(),
51 | "&" => TextFormat::ESCAPE,
52 | "{WORLD}" => $game->getWorldName(),
53 | "{PLAYERS}" => strval(count($game->getPlayers())),
54 | "{TIME}" => strval($game->getProtectTime($player))
55 | ];
56 |
57 | return str_replace(array_keys($index), array_values($index), $msg);
58 | }
59 | }
--------------------------------------------------------------------------------