1+ <?php
2+
3+ declare (strict_types = 1 );
4+
5+ namespace imperazim \components \commands ;
6+
7+ use imperazim \command \Command ;
8+ use imperazim \command \result \CommandResult ;
9+ use imperazim \command \result \CommandFailure ;
10+ use pocketmine \lang \KnownTranslationFactory ;
11+ use pocketmine \network \mcpe \protocol \ProtocolInfo ;
12+ use pocketmine \permission \DefaultPermissionNames ;
13+ use pocketmine \plugin \Plugin ;
14+ use pocketmine \utils \TextFormat ;
15+ use pocketmine \utils \Utils ;
16+ use pocketmine \VersionInfo ;
17+ use function count ;
18+ use function implode ;
19+ use function stripos ;
20+ use function strtolower ;
21+ use function round ;
22+ use function memory_get_usage_real ;
23+ use function floor ;
24+ use const PHP_VERSION ;
25+
26+ /**
27+ * Command to display detailed server information and version data.
28+ *
29+ * This command provides comprehensive details about the server software,
30+ * Minecraft version, performance metrics, system information, and components.
31+ *
32+ * @package imperazim\components\commands
33+ */
34+ final class VersionCommand extends Command {
35+
36+ /**
37+ * Builds the command configuration.
38+ *
39+ * @return array Command settings including name, aliases, description and permission
40+ */
41+ public function onBuild (): array {
42+ return [
43+ 'name ' => 'version ' ,
44+ 'aliases ' => ['ver ' ,
45+ 'about ' ],
46+ 'description ' => 'See detailed information about the server ' ,
47+ 'permission ' => DefaultPermissionNames::COMMAND_VERSION
48+ ];
49+ }
50+
51+ /**
52+ * Executes the version command logic.
53+ *
54+ * Displays server information when no arguments are provided, or searches
55+ * for plugins when arguments are given.
56+ *
57+ * @param CommandResult $result The command execution result container
58+ */
59+ public function onExecute (CommandResult $ result ): void {
60+ $ sender = $ result ->getSender ();
61+ $ args = $ result ->getArgumentsList ();
62+ $ pluginManager = $ sender ->getServer ()->getPluginManager ();
63+
64+ if (count ($ args ) === 0 ) {
65+ $ plugin = $ this ->getPlugin ();
66+ $ server = $ sender ->getServer ();
67+
68+ $ header = "§l§6»§r §lSERVER INFORMATION§r §6« " ;
69+ $ divider = "§8§l»§r§7 " . str_repeat ("─ " , 38 ) . "§8§l« " ;
70+ $ footer = "§8§l»§r§7 " . str_repeat ("─ " , 38 ) . "§8§l« " ;
71+
72+ $ message = [];
73+
74+ $ message [] = $ header ;
75+ $ message [] = $ divider ;
76+ $ message [] = "§e§l»§r §aSOFTWARE " ;
77+ $ message [] = " §8▪ §7Server: §f " . VersionInfo::NAME ;
78+ $ message [] = " §8▪ §7Library: §f " . $ plugin ->getName () . " §8(v " . $ plugin ->getDescription ()->getVersion () . ") " ;
79+
80+ $ versionColor = VersionInfo::IS_DEVELOPMENT_BUILD ? "§c " : "§a " ;
81+ $ message [] = " §8▪ §7Version: " . $ versionColor . VersionInfo::VERSION ()->getFullVersion ();
82+ $ message [] = " §8▪ §7Git: §3 " . VersionInfo::GIT_HASH ();
83+
84+ $ message [] = $ divider ;
85+ $ message [] = "§e§l»§r §aMINECRAFT " ;
86+ $ message [] = " §8▪ §7Version: §f " . ProtocolInfo::MINECRAFT_VERSION_NETWORK ;
87+ $ message [] = " §8▪ §7Protocol: §f " . ProtocolInfo::CURRENT_PROTOCOL ;
88+
89+ $ message [] = $ divider ;
90+ $ message [] = "§e§l»§r §aPERFORMANCE " ;
91+ $ message [] = " §8▪ §7Uptime: §f " . $ this ->formatUptime ($ server ->getTick ());
92+ $ message [] = " §8▪ §7TPS: §f " . round ($ server ->getTicksPerSecond (), 1 );
93+ $ message [] = " §8▪ §7Load: §f " . round ($ server ->getTickUsage (), 1 ) . "% " ;
94+ $ message [] = " §8▪ §7Memory: §f " . $ this ->formatMemory (memory_get_usage ());
95+
96+ $ message [] = $ divider ;
97+ $ message [] = "§e§l»§r §aSYSTEM " ;
98+ $ message [] = " §8▪ §7PHP: §f " . PHP_VERSION ;
99+
100+ $ jitMode = Utils::getOpcacheJitMode ();
101+ if ($ jitMode !== null ) {
102+ $ jitStatus = $ jitMode !== 0
103+ ? "§aEnabled §8(Mode: " . $ jitMode . ") "
104+ : "§cDisabled " ;
105+ } else {
106+ $ jitStatus = "§cNot Supported " ;
107+ }
108+ $ message [] = " §8▪ §7PHP JIT: " . $ jitStatus ;
109+
110+ $ message [] = " §8▪ §7OS: §f " . Utils::getOS ();
111+
112+ $ message [] = $ divider ;
113+ $ message [] = "§e§l»§r §aCOMPONENTS " ;
114+ $ message [] = " §8▪ §7Enabled: §a " . count ($ plugin ->componentsManager ->enabledComponents );
115+ $ message [] = " §8▪ §7Custom: §b " . count (\LibraryComponents::$ customComponents ) - count ($ plugin ->componentsManager ->enabledComponents );
116+
117+ $ message [] = $ footer ;
118+
119+ $ sender ->sendMessage (implode ("\n" , $ message ));
120+ } else {
121+ $ pluginName = implode (" " , $ args );
122+ $ exactPlugin = $ pluginManager ->getPlugin ($ pluginName );
123+
124+ if ($ exactPlugin instanceof Plugin) {
125+ $ this ->describeToSender ($ exactPlugin , $ sender );
126+ return ;
127+ }
128+
129+ $ found = false ;
130+ $ pluginName = strtolower ($ pluginName );
131+ foreach ($ pluginManager ->getPlugins () as $ plugin ) {
132+ if (stripos ($ plugin ->getName (), $ pluginName ) !== false ) {
133+ $ this ->describeToSender ($ plugin , $ sender );
134+ $ found = true ;
135+ }
136+ }
137+
138+ if (!$ found ) {
139+ $ sender ->sendMessage (KnownTranslationFactory::pocketmine_command_version_noSuchPlugin ());
140+ }
141+ }
142+ }
143+
144+ /**
145+ * Formats memory bytes into human-readable string.
146+ *
147+ * @param int $bytes Memory usage in bytes
148+ * @return string Formatted memory string (e.g. "128.54 MB")
149+ */
150+ private function formatMemory (int $ bytes ): string {
151+ $ units = ['B ' ,
152+ 'KB ' ,
153+ 'MB ' ,
154+ 'GB ' ];
155+ $ index = 0 ;
156+
157+ while ($ bytes >= 1024 && $ index < 3 ) {
158+ $ bytes /= 1024 ;
159+ $ index ++;
160+ }
161+
162+ return round ($ bytes , 2 ) . ' ' . $ units [$ index ];
163+ }
164+
165+ /**
166+ * Formats server uptime from ticks into human-readable duration.
167+ *
168+ * @param int $ticks Server uptime in ticks
169+ * @return string Formatted duration (e.g. "2h 15m")
170+ */
171+ private function formatUptime (int $ ticks ): string {
172+ $ seconds = $ ticks / 20 ;
173+ $ minutes = $ seconds / 60 ;
174+ $ hours = $ minutes / 60 ;
175+
176+ if ($ hours >= 1 ) {
177+ return floor ($ hours ) . 'h ' . floor ($ minutes % 60 ) . 'm ' ;
178+ }
179+ if ($ minutes >= 1 ) {
180+ return floor ($ minutes ) . 'm ' . floor ($ seconds % 60 ) . 's ' ;
181+ }
182+ return floor ($ seconds ) . 's ' ;
183+ }
184+
185+ /**
186+ * Handles command execution failures.
187+ *
188+ * @param CommandFailure $failure Container with failure details
189+ */
190+ public function onFailure (CommandFailure $ failure ): void {
191+ $ sender = $ failure ->getSender ();
192+ if ($ failure ->getReason () === CommandFailure::EXECUTION_ERROR ) {
193+ $ data = $ failure ->getData ();
194+ $ sender ->sendMessage ("Fatal error: " . $ failure ->getMessage ());
195+ $ sender ->sendMessage ("Location: " . $ data ['file ' ] . ": " . $ data ['line ' ]);
196+ return ;
197+ }
198+ $ sender ->sendMessage ($ failure ->getMessage ());
199+ }
200+
201+ /**
202+ * Describes plugin details to the sender.
203+ *
204+ * @param Plugin $plugin Plugin to describe
205+ * @param mixed $sender Command sender
206+ */
207+ private function describeToSender (Plugin $ plugin , mixed $ sender ) : void {
208+ $ desc = $ plugin ->getDescription ();
209+ $ sender ->sendMessage (TextFormat::DARK_GREEN . $ desc ->getName () . TextFormat::RESET . " version " . TextFormat::DARK_GREEN . $ desc ->getVersion ());
210+
211+ if ($ desc ->getDescription () !== "" ) {
212+ $ sender ->sendMessage ($ desc ->getDescription ());
213+ }
214+
215+ if ($ desc ->getWebsite () !== "" ) {
216+ $ sender ->sendMessage ("Website: " . $ desc ->getWebsite ());
217+ }
218+
219+ if (count ($ authors = $ desc ->getAuthors ()) > 0 ) {
220+ if (count ($ authors ) === 1 ) {
221+ $ sender ->sendMessage ("Author: " . implode (", " , $ authors ));
222+ } else {
223+ $ sender ->sendMessage ("Authors: " . implode (", " , $ authors ));
224+ }
225+ }
226+ }
227+ }
0 commit comments