levistone 0.9.3__2-cp39-cp39-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of levistone might be problematic. Click here for more details.

@@ -0,0 +1,101 @@
1
+ import platform
2
+ from pathlib import Path
3
+ from typing import Any, Dict
4
+
5
+ import psutil
6
+ from endstone_bstats import AdvancedPie, DrilldownPie, MetricsBase, MetricsConfig, SimplePie, SingleLineChart
7
+
8
+ from endstone import Server, __minecraft_version__
9
+
10
+
11
+ class Metrics(MetricsBase):
12
+ def __init__(self, server: Server):
13
+ self._server = server
14
+
15
+ # Get the config file
16
+ bstats_folder = Path("plugins") / "bstats"
17
+ config_file = bstats_folder / "config.toml"
18
+ self._config = MetricsConfig(config_file, True)
19
+
20
+ super().__init__(
21
+ platform="server-implementation",
22
+ server_uuid=self._config.server_uuid,
23
+ service_id=23296,
24
+ log_errors=self._config.log_errors_enabled,
25
+ log_sent_data=self._config.log_sent_data_enabled,
26
+ log_response_status_text=self._config.log_response_status_text_enabled,
27
+ )
28
+
29
+ self.add_custom_chart(SingleLineChart("players", lambda: len(self._server.online_players)))
30
+ self.add_custom_chart(SimplePie("endstone_version", lambda: self._server.version))
31
+ self.add_custom_chart(SimplePie("minecraft_version", lambda: __minecraft_version__))
32
+ self.add_custom_chart(DrilldownPie("online_mode", self._get_online_mode))
33
+ self.add_custom_chart(DrilldownPie("python_version", self._get_python_version))
34
+ self.add_custom_chart(AdvancedPie("player_platform", self._get_player_platforms))
35
+ self.add_custom_chart(AdvancedPie("player_game_version", self._get_player_game_versions))
36
+
37
+ @property
38
+ def enabled(self) -> bool:
39
+ return self._config.enabled
40
+
41
+ @property
42
+ def service_enabled(self) -> bool:
43
+ return True
44
+
45
+ def append_platform_data(self, platform_data: Dict[str, Any]) -> None:
46
+ os_name = platform.system()
47
+ if os_name == "Windows":
48
+ platform_data["osName"] = f"Windows {platform.release()}"
49
+ platform_data["osVersion"] = platform.version()
50
+ elif os_name == "Linux":
51
+ platform_data["osName"] = "Linux"
52
+ platform_data["osVersion"] = platform.release()
53
+
54
+ os_arch = platform.machine().lower()
55
+ if os_arch == "x86_64":
56
+ os_arch = "amd64"
57
+ platform_data["osArch"] = os_arch
58
+ platform_data["coreCount"] = psutil.cpu_count(logical=False)
59
+
60
+ def log_info(self, message: str) -> None:
61
+ self._server.logger.info(message)
62
+
63
+ def log_error(self, message: str, exception: Exception) -> None:
64
+ self._server.logger.warning(f"{message}: {exception}")
65
+
66
+ def _get_online_mode(self) -> dict[str, dict[str, int]]:
67
+ value = "true" if self._server.online_mode else "false"
68
+ return {
69
+ value: {
70
+ value: 1,
71
+ },
72
+ }
73
+
74
+ def _get_python_version(self) -> dict[str, dict[str, int]]:
75
+ python_impl = platform.python_implementation()
76
+ major, minor, patch = platform.python_version_tuple()
77
+ return {
78
+ f"{python_impl} {major}.{minor}": {
79
+ f"{major}.{minor}.{patch}": 1,
80
+ },
81
+ }
82
+
83
+ def _get_player_platforms(self) -> dict[str, int]:
84
+ result: dict[str, int] = {}
85
+ for player in self._server.online_players:
86
+ if player.device_os not in result:
87
+ result[player.device_os] = 1
88
+ else:
89
+ result[player.device_os] += 1
90
+
91
+ return result
92
+
93
+ def _get_player_game_versions(self) -> dict[str, int]:
94
+ result: dict[str, int] = {}
95
+ for player in self._server.online_players:
96
+ if player.game_version not in result:
97
+ result[player.game_version] = 1
98
+ else:
99
+ result[player.game_version] += 1
100
+
101
+ return result
@@ -0,0 +1,241 @@
1
+ from __future__ import annotations
2
+
3
+ import glob
4
+ import importlib
5
+ import os
6
+ import os.path
7
+ import shutil
8
+ import site
9
+ import subprocess
10
+ import sys
11
+ import traceback
12
+ import warnings
13
+
14
+ import pkginfo
15
+ from importlib_metadata import EntryPoint, distribution, entry_points, metadata
16
+
17
+ from endstone import Server
18
+ from endstone._internal.metrics import Metrics
19
+ from endstone.command import Command
20
+ from endstone.permissions import Permission, PermissionDefault
21
+ from endstone.plugin import Plugin, PluginDescription, PluginLoader, PluginLoadOrder
22
+
23
+ __all__ = ["PythonPluginLoader"]
24
+
25
+ warnings.simplefilter(action="always", category=FutureWarning)
26
+
27
+
28
+ def find_python():
29
+ paths = []
30
+ if os.environ.get("ENDSTONE_PYTHON_EXECUTABLE", None) is not None:
31
+ paths.append(os.environ["ENDSTONE_PYTHON_EXECUTABLE"])
32
+
33
+ if sys.platform == "win32":
34
+ paths.append(os.path.join(sys.base_prefix, "python.exe"))
35
+ else:
36
+ paths.append(
37
+ os.path.join(sys.base_prefix, "bin", "python" + f"{sys.version_info.major}.{sys.version_info.minor}")
38
+ )
39
+ paths.append(os.path.join(sys.base_prefix, "bin", "python" + f"{sys.version_info.major}"))
40
+ paths.append(os.path.join(sys.base_prefix, "bin", "python"))
41
+
42
+ paths = set(paths)
43
+ for path in paths:
44
+ if os.path.isfile(path):
45
+ return path
46
+
47
+ raise RuntimeError(f"Unable to find Python executable. Attempted paths: {paths}")
48
+
49
+
50
+ class PythonPluginLoader(PluginLoader):
51
+ SUPPORTED_API = ["0.5", "0.6", "0.7", "0.8", "0.9"]
52
+
53
+ def __init__(self, server: Server):
54
+ PluginLoader.__init__(self, server)
55
+
56
+ self._plugins = []
57
+
58
+ # fix sys.executable variable
59
+ sys.executable = find_python()
60
+ sys._base_executable = sys.executable
61
+
62
+ # invalidate previously loaded modules (in case of /reload)
63
+ importlib.invalidate_caches()
64
+ for module in list(sys.modules.keys()):
65
+ if module.startswith("endstone_"):
66
+ del sys.modules[module]
67
+
68
+ # prepare the temp site-dir
69
+ self._prefix = os.path.join("plugins", ".local")
70
+ shutil.rmtree(self._prefix, ignore_errors=True)
71
+ for site_dir in site.getsitepackages(prefixes=[self._prefix]):
72
+ site.addsitedir(site_dir)
73
+
74
+ # initialize the metrics
75
+ self._metrics = Metrics(self.server)
76
+
77
+ def __del__(self):
78
+ self._metrics.shutdown()
79
+
80
+ @staticmethod
81
+ def _build_commands(commands: dict) -> list[Command]:
82
+ results = []
83
+ for name, command in commands.items():
84
+ command = Command(name, **command)
85
+ results.append(command)
86
+ return results
87
+
88
+ @staticmethod
89
+ def _build_permissions(permissions: dict) -> list[Permission]:
90
+ results = []
91
+ for name, permission in permissions.items():
92
+ if "default" in permission:
93
+ value = permission["default"]
94
+ if isinstance(value, bool):
95
+ permission["default"] = PermissionDefault.TRUE if value else PermissionDefault.FALSE
96
+ elif isinstance(value, str):
97
+ permission["default"] = PermissionDefault.__members__[value.strip().replace(" ", "_").upper()]
98
+ elif not isinstance(value, PermissionDefault):
99
+ raise TypeError(f"Invalid value for default permission: {value}")
100
+
101
+ permission = Permission(name, **permission)
102
+ results.append(permission)
103
+ return results
104
+
105
+ def load_plugin(self, file: str) -> Plugin | None:
106
+ env = os.environ.copy()
107
+ env.pop("LD_PRELOAD", "")
108
+
109
+ dist_name = pkginfo.Wheel(file).name
110
+ subprocess.run(
111
+ [
112
+ sys.executable,
113
+ "-m",
114
+ "pip",
115
+ "install",
116
+ file,
117
+ "--prefix",
118
+ self._prefix,
119
+ "--quiet",
120
+ "--no-warn-script-location",
121
+ "--disable-pip-version-check",
122
+ ],
123
+ env=env,
124
+ )
125
+
126
+ eps = distribution(dist_name).entry_points.select(group="endstone")
127
+ for ep in eps:
128
+ plugin = self._load_plugin_from_ep(ep)
129
+ if plugin:
130
+ return plugin
131
+
132
+ return None
133
+
134
+ def load_plugins(self, directory: str) -> list[Plugin]:
135
+ loaded_plugins = []
136
+
137
+ if not self._plugins:
138
+ eps = entry_points(group="endstone")
139
+ for ep in eps:
140
+ plugin = self._load_plugin_from_ep(ep)
141
+ if plugin:
142
+ loaded_plugins.append(plugin)
143
+
144
+ for file in glob.glob(os.path.join(directory, "*.whl")):
145
+ plugin = self.load_plugin(file)
146
+ if plugin:
147
+ loaded_plugins.append(plugin)
148
+
149
+ return loaded_plugins
150
+
151
+ def _load_plugin_from_ep(self, ep: EntryPoint) -> Plugin | None:
152
+ # enforce naming convention
153
+ if not ep.dist.name.replace("_", "-").startswith("endstone-"):
154
+ self.server.logger.error(
155
+ f"Error occurred when trying to load plugin from entry point '{ep.name}': Invalid name."
156
+ )
157
+ self.server.logger.error(
158
+ f"The name of distribution ({ep.dist.name}) does not start with 'endstone-' or 'endstone_'."
159
+ )
160
+ return None
161
+
162
+ dist_name = "endstone-" + ep.name.replace("_", "-")
163
+ if ep.dist.name.replace("_", "-") != dist_name:
164
+ self.server.logger.error(
165
+ f"Error occurred when trying to load plugin from entry point '{ep.name}': Invalid name."
166
+ )
167
+ self.server.logger.error("You need to make **ONE** of the following changes.")
168
+ self.server.logger.error(
169
+ f"* If you intend to use the current entry point name ({ep.name}), "
170
+ f"please change the distribution name from '{ep.dist.name}' to '{dist_name}'."
171
+ )
172
+ self.server.logger.error(
173
+ f"* If not, please change the entry point name from '{ep.name}' to '{ep.dist.name[9:]}'."
174
+ )
175
+ return None
176
+
177
+ # get distribution metadata
178
+ try:
179
+ plugin_metadata = metadata(ep.dist.name).json
180
+ cls = ep.load()
181
+ except BaseException as e:
182
+ self.server.logger.error(f"Error occurred when trying to load plugin from entry point '{ep.name}':")
183
+ self.server.logger.error("".join(traceback.format_exception(e)))
184
+ return None
185
+
186
+ # prepare plugin description
187
+ cls_attr = dict(cls.__dict__)
188
+ name = cls_attr.pop("name", ep.name.replace("-", "_"))
189
+ version = cls_attr.pop("version", plugin_metadata["version"])
190
+
191
+ api_version = cls_attr.pop("api_version", None)
192
+ if api_version is None:
193
+ self.server.logger.warning(
194
+ f"Plugin '{name}' does not specify an API version. This may prevent the plugin from loading in "
195
+ f"future releases."
196
+ )
197
+ elif api_version not in self.SUPPORTED_API:
198
+ self.server.logger.error(
199
+ f"Error occurred when trying to load plugin '{name}': plugin was designed for API version: "
200
+ f"{api_version} which is not compatible with this server."
201
+ )
202
+ return None
203
+
204
+ load = cls_attr.pop("load", None)
205
+ if load is not None:
206
+ if isinstance(load, str):
207
+ load = PluginLoadOrder.__members__[load.strip().replace(" ", "_").upper()]
208
+ elif not isinstance(load, PluginLoadOrder):
209
+ raise TypeError(f"Invalid value for load order: {load}")
210
+
211
+ description = cls_attr.pop("description", plugin_metadata.get("summary", None))
212
+ authors = cls_attr.pop("authors", plugin_metadata.get("author_email", "").split(","))
213
+ website = cls_attr.pop("website", "; ".join(plugin_metadata.get("project_url", [])))
214
+
215
+ commands = cls_attr.pop("commands", {})
216
+ commands = self._build_commands(commands)
217
+
218
+ permissions = cls_attr.pop("permissions", {})
219
+ permissions = self._build_permissions(permissions)
220
+
221
+ plugin_description = PluginDescription(
222
+ name=name,
223
+ version=version,
224
+ load=load,
225
+ description=description,
226
+ authors=authors,
227
+ website=website,
228
+ commands=commands,
229
+ permissions=permissions,
230
+ **cls_attr,
231
+ )
232
+
233
+ # instantiate plugin
234
+ plugin = cls()
235
+ if not isinstance(plugin, Plugin):
236
+ self.server.logger.error(f"Main class {ep.value} does not extend endstone.plugin.Plugin")
237
+ return None
238
+
239
+ plugin._description = plugin_description
240
+ self._plugins.append(plugin)
241
+ return plugin
@@ -0,0 +1,14 @@
1
+ TYPE_CHECKING = False
2
+ if TYPE_CHECKING:
3
+ from typing import Tuple, Union
4
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
5
+ else:
6
+ VERSION_TUPLE = object
7
+
8
+ version: str
9
+ __version__: str
10
+ __version_tuple__: VERSION_TUPLE
11
+ version_tuple: VERSION_TUPLE
12
+
13
+ __version__ = version = '0.9.3'
14
+ __version_tuple__ = version_tuple = (0, 9, 3, '')
endstone/actor.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import Actor, Mob
2
+
3
+ __all__ = ["Actor", "Mob"]
endstone/ban.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import IpBanEntry, IpBanList, PlayerBanEntry, PlayerBanList
2
+
3
+ __all__ = ["IpBanEntry", "IpBanList", "PlayerBanEntry", "PlayerBanList"]
endstone/block.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import Block, BlockData, BlockFace, BlockState
2
+
3
+ __all__ = ["Block", "BlockData", "BlockFace", "BlockState"]
endstone/boss.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import BarColor, BarFlag, BarStyle, BossBar
2
+
3
+ __all__ = ["BossBar", "BarColor", "BarFlag", "BarStyle"]
endstone/command.py ADDED
@@ -0,0 +1,17 @@
1
+ from endstone._internal.endstone_python import (
2
+ Command,
3
+ CommandExecutor,
4
+ CommandSender,
5
+ CommandSenderWrapper,
6
+ ConsoleCommandSender,
7
+ ProxiedCommandSender,
8
+ )
9
+
10
+ __all__ = [
11
+ "Command",
12
+ "CommandExecutor",
13
+ "CommandSender",
14
+ "CommandSenderWrapper",
15
+ "ConsoleCommandSender",
16
+ "ProxiedCommandSender",
17
+ ]
@@ -0,0 +1,4 @@
1
+ [settings]
2
+ # Allow clients to use their own packs when texturepack-required is set to true in server.properties.
3
+ # Has no effect if texturepack-required is false.
4
+ allow-client-packs = false
endstone/damage.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import DamageSource
2
+
3
+ __all__ = ["DamageSource"]
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import Enchantment
2
+
3
+ __all__ = ["Enchantment"]
endstone/event.py ADDED
@@ -0,0 +1,123 @@
1
+ from endstone._internal.endstone_python import (
2
+ ActorDamageEvent,
3
+ ActorDeathEvent,
4
+ ActorEvent,
5
+ ActorExplodeEvent,
6
+ ActorKnockbackEvent,
7
+ ActorRemoveEvent,
8
+ ActorSpawnEvent,
9
+ ActorTeleportEvent,
10
+ BlockBreakEvent,
11
+ BlockEvent,
12
+ BlockPlaceEvent,
13
+ BroadcastMessageEvent,
14
+ Cancellable,
15
+ ChunkEvent,
16
+ ChunkLoadEvent,
17
+ ChunkUnloadEvent,
18
+ DimensionEvent,
19
+ Event,
20
+ EventPriority,
21
+ LevelEvent,
22
+ MobEvent,
23
+ PacketReceiveEvent,
24
+ PacketSendEvent,
25
+ PlayerChatEvent,
26
+ PlayerCommandEvent,
27
+ PlayerDeathEvent,
28
+ PlayerDropItemEvent,
29
+ PlayerEmoteEvent,
30
+ PlayerEvent,
31
+ PlayerGameModeChangeEvent,
32
+ PlayerInteractActorEvent,
33
+ PlayerInteractEvent,
34
+ PlayerItemConsumeEvent,
35
+ PlayerJoinEvent,
36
+ PlayerJumpEvent,
37
+ PlayerKickEvent,
38
+ PlayerLoginEvent,
39
+ PlayerMoveEvent,
40
+ PlayerPickupItemEvent,
41
+ PlayerQuitEvent,
42
+ PlayerRespawnEvent,
43
+ PlayerTeleportEvent,
44
+ PluginDisableEvent,
45
+ PluginEnableEvent,
46
+ ScriptMessageEvent,
47
+ ServerCommandEvent,
48
+ ServerEvent,
49
+ ServerListPingEvent,
50
+ ServerLoadEvent,
51
+ ThunderChangeEvent,
52
+ WeatherChangeEvent,
53
+ WeatherEvent,
54
+ )
55
+
56
+ __all__ = [
57
+ "event_handler",
58
+ "ActorEvent",
59
+ "ActorDamageEvent",
60
+ "ActorDeathEvent",
61
+ "ActorExplodeEvent",
62
+ "ActorKnockbackEvent",
63
+ "ActorRemoveEvent",
64
+ "ActorSpawnEvent",
65
+ "ActorTeleportEvent",
66
+ "BlockEvent",
67
+ "BlockBreakEvent",
68
+ "BlockPlaceEvent",
69
+ "Cancellable",
70
+ "ChunkEvent",
71
+ "ChunkLoadEvent",
72
+ "ChunkUnloadEvent",
73
+ "DimensionEvent",
74
+ "Event",
75
+ "EventPriority",
76
+ "LevelEvent",
77
+ "MobEvent",
78
+ "PacketReceiveEvent",
79
+ "PacketSendEvent",
80
+ "PlayerEvent",
81
+ "PlayerChatEvent",
82
+ "PlayerCommandEvent",
83
+ "PlayerDeathEvent",
84
+ "PlayerDropItemEvent",
85
+ "PlayerEmoteEvent",
86
+ "PlayerGameModeChangeEvent",
87
+ "PlayerInteractEvent",
88
+ "PlayerInteractActorEvent",
89
+ "PlayerItemConsumeEvent",
90
+ "PlayerJoinEvent",
91
+ "PlayerJumpEvent",
92
+ "PlayerKickEvent",
93
+ "PlayerLoginEvent",
94
+ "PlayerMoveEvent",
95
+ "PlayerPickupItemEvent",
96
+ "PlayerQuitEvent",
97
+ "PlayerRespawnEvent",
98
+ "PlayerTeleportEvent",
99
+ "BroadcastMessageEvent",
100
+ "PluginEnableEvent",
101
+ "PluginDisableEvent",
102
+ "ScriptMessageEvent",
103
+ "ServerEvent",
104
+ "ServerCommandEvent",
105
+ "ServerListPingEvent",
106
+ "ServerLoadEvent",
107
+ "ThunderChangeEvent",
108
+ "WeatherEvent",
109
+ "WeatherChangeEvent",
110
+ ]
111
+
112
+
113
+ def event_handler(func=None, *, priority: EventPriority = EventPriority.NORMAL, ignore_cancelled: bool = False):
114
+ def decorator(f):
115
+ setattr(f, "_is_event_handler", True)
116
+ setattr(f, "_priority", priority)
117
+ setattr(f, "_ignore_cancelled", ignore_cancelled)
118
+ return f
119
+
120
+ if func:
121
+ return decorator(func)
122
+
123
+ return decorator
endstone/form.py ADDED
@@ -0,0 +1,27 @@
1
+ from endstone._internal.endstone_python import (
2
+ ActionForm,
3
+ Divider,
4
+ Dropdown,
5
+ Header,
6
+ Label,
7
+ MessageForm,
8
+ ModalForm,
9
+ Slider,
10
+ StepSlider,
11
+ TextInput,
12
+ Toggle,
13
+ )
14
+
15
+ __all__ = [
16
+ "ActionForm",
17
+ "MessageForm",
18
+ "ModalForm",
19
+ "Dropdown",
20
+ "Label",
21
+ "Slider",
22
+ "StepSlider",
23
+ "TextInput",
24
+ "Toggle",
25
+ "Divider",
26
+ "Header",
27
+ ]
endstone/inventory.py ADDED
@@ -0,0 +1,21 @@
1
+ from endstone._internal.endstone_python import (
2
+ EquipmentSlot,
3
+ Inventory,
4
+ ItemFactory,
5
+ ItemMeta,
6
+ ItemStack,
7
+ ItemType,
8
+ MapMeta,
9
+ PlayerInventory,
10
+ )
11
+
12
+ __all__ = [
13
+ "EquipmentSlot",
14
+ "Inventory",
15
+ "ItemFactory",
16
+ "ItemMeta",
17
+ "ItemStack",
18
+ "ItemType",
19
+ "MapMeta",
20
+ "PlayerInventory",
21
+ ]
endstone/lang.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import Language, Translatable
2
+
3
+ __all__ = ["Language", "Translatable"]
endstone/level.py ADDED
@@ -0,0 +1,9 @@
1
+ from endstone._internal.endstone_python import Chunk, Dimension, Level, Location, Position
2
+
3
+ __all__ = [
4
+ "Chunk",
5
+ "Dimension",
6
+ "Level",
7
+ "Location",
8
+ "Position",
9
+ ]
endstone/map.py ADDED
@@ -0,0 +1,3 @@
1
+ from endstone._internal.endstone_python import MapCanvas, MapRenderer, MapView
2
+
3
+ __all__ = ["MapCanvas", "MapRenderer", "MapView"]
@@ -0,0 +1,9 @@
1
+ from endstone._internal.endstone_python import (
2
+ Permissible,
3
+ Permission,
4
+ PermissionAttachment,
5
+ PermissionAttachmentInfo,
6
+ PermissionDefault,
7
+ )
8
+
9
+ __all__ = ["Permission", "Permissible", "PermissionAttachment", "PermissionAttachmentInfo", "PermissionDefault"]