pumpkin-api-py 0.1.0.dev0__py3-none-any.whl → 0.1.0.dev2__py3-none-any.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.
Files changed (83) hide show
  1. pumpkin_api/__init__.py +39 -137
  2. pumpkin_api/build.py +53 -19
  3. pumpkin_api/impl.py +216 -0
  4. pumpkin_api/{wit_files/entity.wit → wit/block-entity.wit} +2 -2
  5. pumpkin_api/wit/boss-bar.wit +82 -0
  6. pumpkin_api/{wit_files → wit}/command.wit +3 -2
  7. pumpkin_api/{wit_files → wit}/common.wit +22 -0
  8. pumpkin_api/wit/entity.wit +3 -0
  9. pumpkin_api/{wit_files → wit}/event.wit +8 -1
  10. pumpkin_api/{wit_files → wit}/gui.wit +12 -0
  11. pumpkin_api/{wit_files → wit}/metadata.wit +2 -0
  12. pumpkin_api/{wit_files → wit}/player.wit +16 -0
  13. pumpkin_api/{wit_files → wit}/plugin.wit +7 -4
  14. pumpkin_api/wit/server.wit +86 -0
  15. pumpkin_api/{wit_files → wit}/world.wit +42 -1
  16. {pumpkin_api_py-0.1.0.dev0.dist-info → pumpkin_api_py-0.1.0.dev2.dist-info}/METADATA +9 -16
  17. pumpkin_api_py-0.1.0.dev2.dist-info/RECORD +53 -0
  18. pumpkin_api_py-0.1.0.dev2.dist-info/entry_points.txt +2 -0
  19. {pumpkin_api/wit_files/wit_world → wit_world}/exports/metadata.py +1 -0
  20. pumpkin_api/wit_files/wit_world/imports/entity.py → wit_world/imports/block_entity.py +2 -2
  21. wit_world/imports/boss_bar.py +137 -0
  22. {pumpkin_api/wit/wit_world → wit_world}/imports/command.py +4 -4
  23. {pumpkin_api/wit/wit_world → wit_world}/imports/common.py +23 -0
  24. {pumpkin_api/wit_files/wit_world → wit_world}/imports/event.py +23 -12
  25. {pumpkin_api/wit_files/wit_world → wit_world}/imports/gui.py +20 -0
  26. {pumpkin_api/wit_files/wit_world → wit_world}/imports/player.py +30 -0
  27. wit_world/imports/server.py +148 -0
  28. {pumpkin_api/wit/wit_world → wit_world}/imports/world.py +73 -0
  29. pumpkin_api/app.py +0 -8
  30. pumpkin_api/wit/wit_world/exports/metadata.py +0 -22
  31. pumpkin_api/wit/wit_world/imports/entity.py +0 -84
  32. pumpkin_api/wit/wit_world/imports/event.py +0 -434
  33. pumpkin_api/wit/wit_world/imports/gui.py +0 -61
  34. pumpkin_api/wit/wit_world/imports/player.py +0 -350
  35. pumpkin_api/wit/wit_world/imports/server.py +0 -75
  36. pumpkin_api/wit_files/componentize_py_async_support/__init__.py +0 -529
  37. pumpkin_api/wit_files/componentize_py_async_support/futures.py +0 -93
  38. pumpkin_api/wit_files/componentize_py_async_support/streams.py +0 -189
  39. pumpkin_api/wit_files/componentize_py_runtime.pyi +0 -34
  40. pumpkin_api/wit_files/componentize_py_types.py +0 -19
  41. pumpkin_api/wit_files/poll_loop.py +0 -440
  42. pumpkin_api/wit_files/server.wit +0 -41
  43. pumpkin_api/wit_files/wit_world/__init__.py +0 -70
  44. pumpkin_api/wit_files/wit_world/exports/__init__.py +0 -19
  45. pumpkin_api/wit_files/wit_world/imports/__init__.py +0 -0
  46. pumpkin_api/wit_files/wit_world/imports/command.py +0 -539
  47. pumpkin_api/wit_files/wit_world/imports/common.py +0 -213
  48. pumpkin_api/wit_files/wit_world/imports/context.py +0 -70
  49. pumpkin_api/wit_files/wit_world/imports/i18n.py +0 -35
  50. pumpkin_api/wit_files/wit_world/imports/logging.py +0 -30
  51. pumpkin_api/wit_files/wit_world/imports/permission.py +0 -71
  52. pumpkin_api/wit_files/wit_world/imports/scheduler.py +0 -44
  53. pumpkin_api/wit_files/wit_world/imports/scoreboard.py +0 -95
  54. pumpkin_api/wit_files/wit_world/imports/server.py +0 -75
  55. pumpkin_api/wit_files/wit_world/imports/text.py +0 -89
  56. pumpkin_api/wit_files/wit_world/imports/world.py +0 -215
  57. pumpkin_api_py-0.1.0.dev0.dist-info/RECORD +0 -75
  58. pumpkin_api_py-0.1.0.dev0.dist-info/entry_points.txt +0 -2
  59. {pumpkin_api/wit/componentize_py_async_support → componentize_py_async_support}/__init__.py +0 -0
  60. {pumpkin_api/wit/componentize_py_async_support → componentize_py_async_support}/futures.py +0 -0
  61. {pumpkin_api/wit/componentize_py_async_support → componentize_py_async_support}/streams.py +0 -0
  62. /pumpkin_api/wit/componentize_py_runtime.pyi → /componentize_py_runtime.pyi +0 -0
  63. /pumpkin_api/wit/componentize_py_types.py → /componentize_py_types.py +0 -0
  64. /pumpkin_api/wit/poll_loop.py → /poll_loop.py +0 -0
  65. /pumpkin_api/{wit_files → wit}/context.wit +0 -0
  66. /pumpkin_api/{wit_files → wit}/i18n.wit +0 -0
  67. /pumpkin_api/{wit_files → wit}/log.wit +0 -0
  68. /pumpkin_api/{wit_files → wit}/permission.wit +0 -0
  69. /pumpkin_api/{wit_files → wit}/scheduler.wit +0 -0
  70. /pumpkin_api/{wit_files → wit}/scoreboard.wit +0 -0
  71. /pumpkin_api/{wit_files → wit}/text.wit +0 -0
  72. {pumpkin_api_py-0.1.0.dev0.dist-info → pumpkin_api_py-0.1.0.dev2.dist-info}/WHEEL +0 -0
  73. {pumpkin_api_py-0.1.0.dev0.dist-info → pumpkin_api_py-0.1.0.dev2.dist-info}/licenses/LICENSE +0 -0
  74. {pumpkin_api/wit/wit_world → wit_world}/__init__.py +0 -0
  75. {pumpkin_api/wit/wit_world → wit_world}/exports/__init__.py +0 -0
  76. {pumpkin_api/wit/wit_world → wit_world}/imports/__init__.py +0 -0
  77. {pumpkin_api/wit/wit_world → wit_world}/imports/context.py +0 -0
  78. {pumpkin_api/wit/wit_world → wit_world}/imports/i18n.py +0 -0
  79. {pumpkin_api/wit/wit_world → wit_world}/imports/logging.py +0 -0
  80. {pumpkin_api/wit/wit_world → wit_world}/imports/permission.py +0 -0
  81. {pumpkin_api/wit/wit_world → wit_world}/imports/scheduler.py +0 -0
  82. {pumpkin_api/wit/wit_world → wit_world}/imports/scoreboard.py +0 -0
  83. {pumpkin_api/wit/wit_world → wit_world}/imports/text.py +0 -0
pumpkin_api/__init__.py CHANGED
@@ -1,139 +1,41 @@
1
- # pumpkin-api-py: Pumpkin plugin API for Python
2
- # Copyright (C) 2024 alex
3
- #
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU General Public License as published by
6
- # the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
-
17
- import sys
18
- import os
19
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'wit'))
20
-
21
- from typing import Dict, Callable, Any, Type, Optional, List, Union
22
-
23
- from .wit.wit_world.imports import (
24
- server, event, command, context, text, scheduler,
25
- logging, gui, scoreboard, i18n, player, world,
26
- common, entity, permission
1
+ from wit_world.exports import metadata
2
+ from wit_world.imports import (
3
+ block_entity,
4
+ command,
5
+ common,
6
+ context,
7
+ event,
8
+ gui,
9
+ i18n,
10
+ logging,
11
+ permission,
12
+ player,
13
+ scheduler,
14
+ scoreboard,
15
+ server,
16
+ text,
17
+ world,
27
18
  )
28
- from .wit.wit_world.exports import metadata
29
- from componentize_py_types import Err
30
-
31
- class PluginMetadata(metadata.PluginMetadata):
32
- pass
33
-
34
- _PLUGIN_INSTANCE: Optional['Plugin'] = None
35
- _EVENT_HANDLERS: Dict[int, Callable[[server.Server, Any], Any]] = {}
36
- _COMMAND_HANDLERS: Dict[int, Callable[[command.CommandSender, server.Server, command.ConsumedArgs], int]] = {}
37
- _TASK_HANDLERS: Dict[int, Callable[[server.Server], None]] = {}
38
- _NEXT_HANDLER_ID: int = 0
39
-
40
- def _get_next_handler_id() -> int:
41
- global _NEXT_HANDLER_ID
42
- handler_id = _NEXT_HANDLER_ID
43
- _NEXT_HANDLER_ID += 1
44
- return handler_id
45
-
46
- class Plugin:
47
- def __init__(self):
48
- self._pending_events = []
49
-
50
- def metadata(self) -> PluginMetadata:
51
- raise NotImplementedError
52
-
53
- def on_load(self, ctx: context.Context) -> None:
54
- for event_type, handler, priority, blocking in self._pending_events:
55
- self.register_event(ctx, event_type, handler, priority, blocking)
56
-
57
- def on_unload(self, ctx: context.Context) -> None:
58
- pass
59
-
60
- def register_event(self, ctx: context.Context, event_type: event.EventType, handler: Callable[[server.Server, Any], Any], priority=event.EventPriority.NORMAL, blocking=True):
61
- handler_id = _get_next_handler_id()
62
- _EVENT_HANDLERS[handler_id] = handler
63
- ctx.register_event(handler_id, event_type, priority, blocking)
64
-
65
- def register_command(self, ctx: context.Context, cmd: command.Command, handler: Callable[[command.CommandSender, server.Server, command.ConsumedArgs], int], permission: str = ""):
66
- handler_id = _get_next_handler_id()
67
- _COMMAND_HANDLERS[handler_id] = handler
68
- cmd.execute_with_handler_id(handler_id)
69
- ctx.register_command(cmd, permission)
70
-
71
- def schedule_delayed_task(self, delay_ticks: int, handler: Callable[[server.Server], None]) -> int:
72
- handler_id = _get_next_handler_id()
73
- _TASK_HANDLERS[handler_id] = handler
74
- return scheduler.schedule_delayed_task(handler_id, delay_ticks)
75
-
76
- def schedule_repeating_task(self, delay_ticks: int, period_ticks: int, handler: Callable[[server.Server], None]) -> int:
77
- handler_id = _get_next_handler_id()
78
- _TASK_HANDLERS[handler_id] = handler
79
- return scheduler.schedule_repeating_task(handler_id, delay_ticks, period_ticks)
80
-
81
- def event_handler(self, event_type: event.EventType, priority=event.EventPriority.NORMAL, blocking=True):
82
- def decorator(func):
83
- self._pending_events.append((event_type, func, priority, blocking))
84
- return func
85
- return decorator
86
-
87
- def register_plugin(plugin_class: Type[Plugin]):
88
- global _PLUGIN_INSTANCE
89
- _PLUGIN_INSTANCE = plugin_class()
90
-
91
- class WitWorldImpl:
92
- def init_plugin(self) -> None:
93
- pass
94
-
95
- def on_load(self, ctx: context.Context) -> None:
96
- if _PLUGIN_INSTANCE:
97
- try:
98
- # In WIT, on-load returns result<_, string>
99
- # componentize-py handles this by expecting None or raising Err
100
- _PLUGIN_INSTANCE.on_load(ctx)
101
- except Exception as e:
102
- raise Err(str(e))
103
-
104
- def on_unload(self, ctx: context.Context) -> None:
105
- if _PLUGIN_INSTANCE:
106
- try:
107
- _PLUGIN_INSTANCE.on_unload(ctx)
108
- except Exception as e:
109
- raise Err(str(e))
110
-
111
- def handle_event(self, event_id: int, srv: server.Server, evt: event.Event) -> event.Event:
112
- if event_id in _EVENT_HANDLERS:
113
- # Pass the inner event data to the handler
114
- result = _EVENT_HANDLERS[event_id](srv, evt.value)
115
- # If the handler returns something, assume it's the updated event data
116
- if result is not None:
117
- evt.value = result
118
- return evt
119
-
120
- def handle_command(self, command_id: int, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int:
121
- if command_id in _COMMAND_HANDLERS:
122
- try:
123
- return _COMMAND_HANDLERS[command_id](sender, srv, args)
124
- except Exception as e:
125
- if hasattr(e, 'value') and isinstance(e.value, (command.CommandError_InvalidConsumption, command.CommandError_InvalidRequirement, command.CommandError_PermissionDenied, command.CommandError_CommandFailed)):
126
- raise e
127
- raise Err(command.CommandError_CommandFailed(text.TextComponent_text(str(e))))
128
-
129
- raise Err(command.CommandError_CommandFailed(text.TextComponent_text(f"no handler registered for command id {command_id}")))
130
-
131
- def handle_task(self, handler_id: int, srv: server.Server) -> None:
132
- if handler_id in _TASK_HANDLERS:
133
- _TASK_HANDLERS[handler_id](srv)
134
19
 
135
- class MetadataImpl:
136
- def get_metadata(self) -> metadata.PluginMetadata:
137
- if _PLUGIN_INSTANCE:
138
- return _PLUGIN_INSTANCE.metadata()
139
- return PluginMetadata("unknown", "0.0.0", [], "No metadata")
20
+ from .impl import Plugin, register_plugin
21
+
22
+ __all__ = [
23
+ "Plugin",
24
+ "register_plugin",
25
+ "block_entity",
26
+ "command",
27
+ "common",
28
+ "context",
29
+ "event",
30
+ "gui",
31
+ "i18n",
32
+ "logging",
33
+ "metadata",
34
+ "permission",
35
+ "player",
36
+ "scheduler",
37
+ "server",
38
+ "scoreboard",
39
+ "text",
40
+ "world",
41
+ ]
pumpkin_api/build.py CHANGED
@@ -1,35 +1,69 @@
1
1
  import argparse
2
- import subprocess
3
- import sys
4
2
  import os
3
+ import tempfile
4
+
5
+ import componentize_py
6
+
7
+
8
+ def _build_entrypoint_wrapper(app_module: str, wrapper_dir: str) -> str:
9
+ wrapper_name = "_pumpkin_componentize_entry"
10
+ wrapper_path = os.path.join(wrapper_dir, f"{wrapper_name}.py")
11
+
12
+ with open(wrapper_path, "w", encoding="utf-8") as wrapper_file:
13
+ wrapper_file.write(
14
+ "from pathlib import Path\n"
15
+ "from pumpkin_api.impl import MetadataImpl, WitWorldImpl\n\n"
16
+ "WitWorld = WitWorldImpl\n"
17
+ "Metadata = MetadataImpl\n\n"
18
+ f"import {app_module}\n"
19
+ )
20
+ return wrapper_name
21
+
5
22
 
6
23
  def main():
7
24
  parser = argparse.ArgumentParser(description="Build a Pumpkin Python plugin")
8
- parser.add_argument("app_module", help="The python module containing the plugin (e.g. 'main')")
9
- parser.add_argument("-o", "--output", default="plugin.wasm", help="Output wasm file name")
25
+ parser.add_argument(
26
+ "app_module", help="The python module containing the plugin (e.g. 'main')"
27
+ )
28
+ parser.add_argument(
29
+ "-o", "--output", default="plugin.wasm", help="Output wasm file name"
30
+ )
10
31
  args = parser.parse_args()
11
32
 
12
33
  import pumpkin_api
34
+
13
35
  pkg_dir = os.path.dirname(pumpkin_api.__file__)
14
- wit_dir = os.path.join(pkg_dir, "wit_files")
15
-
36
+ # In source checkout, they are in wit_files/repo/...
37
+ # In installed package, they are directly in wit_files/
38
+ wit_dir = os.path.join(
39
+ pkg_dir, "..", "..", "wit", "repo", "pumpkin-plugin-wit", "v0.1.0"
40
+ )
41
+ if not os.path.exists(wit_dir):
42
+ wit_dir = os.path.join(pkg_dir, "wit")
43
+
16
44
  # Pass the parent directory of pumpkin_api to componentize-py so it can resolve `pumpkin_api` module
17
45
  # in case it's not installed in a standard site-packages (e.g. editable install or no venv)
18
46
  pkg_parent_dir = os.path.dirname(pkg_dir)
19
47
 
20
- cmd = [
21
- "componentize-py",
22
- "-d", wit_dir,
23
- "-w", "plugin",
24
- "componentize", args.app_module,
25
- "-o", args.output,
26
- "-p", ".",
27
- "-p", pkg_parent_dir
28
- ]
29
-
30
- print(f"Running: {' '.join(cmd)}")
31
- result = subprocess.run(cmd)
32
- sys.exit(result.returncode)
48
+ with tempfile.TemporaryDirectory(prefix="pumpkin-api-build-") as wrapper_dir:
49
+ wrapper_module = _build_entrypoint_wrapper(args.app_module, wrapper_dir)
50
+
51
+ componentize_py.componentize(
52
+ wit_path=[wit_dir],
53
+ worlds=[],
54
+ features=[],
55
+ all_features=False,
56
+ world_module="wit_world",
57
+ python_path=[".", pkg_parent_dir, wrapper_dir],
58
+ module_worlds=[],
59
+ app_name=wrapper_module,
60
+ output_path=args.output,
61
+ stub_wasi=False,
62
+ import_interface_names=[],
63
+ export_interface_names=[],
64
+ full_names=False,
65
+ )
66
+
33
67
 
34
68
  if __name__ == "__main__":
35
69
  main()
pumpkin_api/impl.py ADDED
@@ -0,0 +1,216 @@
1
+ # pumpkin-api-py: Pumpkin plugin API for Python
2
+ # Copyright (C) 2024 alex
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ import traceback
18
+ from typing import Any, Callable, Dict, Optional, Type
19
+
20
+ import wit_world
21
+ import wit_world.exports
22
+ from componentize_py_types import Err
23
+ from wit_world.exports import metadata
24
+ from wit_world.imports import command, context, event, scheduler, server, text
25
+
26
+ _PLUGIN_INSTANCE: Optional["Plugin"] = None
27
+ _EVENT_HANDLERS: Dict[int, Callable[[server.Server, Any], Any]] = {}
28
+ _COMMAND_HANDLERS: Dict[
29
+ int, Callable[[command.CommandSender, server.Server, command.ConsumedArgs], int]
30
+ ] = {}
31
+ _TASK_HANDLERS: Dict[int, Callable[[server.Server], None]] = {}
32
+ _NEXT_HANDLER_ID: int = 0
33
+
34
+
35
+ def _exception_message(exc: Exception) -> str:
36
+ if hasattr(exc, "value"):
37
+ value = getattr(exc, "value")
38
+ if value is not None:
39
+ return str(value)
40
+ message = str(exc)
41
+ if message:
42
+ return message
43
+ return "".join(traceback.format_exception_only(type(exc), exc)).strip()
44
+
45
+
46
+ def _format_exception(exc: Exception) -> str:
47
+ details = "".join(
48
+ traceback.format_exception(type(exc), exc, exc.__traceback__)
49
+ ).strip()
50
+ if details:
51
+ return details
52
+ return _exception_message(exc)
53
+
54
+
55
+ def _get_next_handler_id() -> int:
56
+ global _NEXT_HANDLER_ID
57
+ handler_id = _NEXT_HANDLER_ID
58
+ _NEXT_HANDLER_ID += 1
59
+ return handler_id
60
+
61
+
62
+ class Plugin:
63
+ def __init__(self):
64
+ self._pending_events = []
65
+
66
+ def metadata(self) -> metadata.PluginMetadata:
67
+ raise NotImplementedError
68
+
69
+ def on_load(self, ctx: context.Context) -> None:
70
+ for event_type, handler, priority, blocking in self._pending_events:
71
+ self.register_event(ctx, event_type, handler, priority, blocking)
72
+
73
+ def on_unload(self, ctx: context.Context) -> None:
74
+ pass
75
+
76
+ def register_event(
77
+ self,
78
+ ctx: context.Context,
79
+ event_type: event.EventType,
80
+ handler: Callable[[server.Server, Any], Any],
81
+ priority=event.EventPriority.NORMAL,
82
+ blocking=True,
83
+ ):
84
+ handler_id = _get_next_handler_id()
85
+ _EVENT_HANDLERS[handler_id] = handler
86
+ ctx.register_event(handler_id, event_type, priority, blocking)
87
+
88
+ def register_command(
89
+ self,
90
+ ctx: context.Context,
91
+ cmd: command.Command,
92
+ handler: Callable[
93
+ [command.CommandSender, server.Server, command.ConsumedArgs], int
94
+ ],
95
+ permission: str = "",
96
+ ):
97
+ handler_id = _get_next_handler_id()
98
+ _COMMAND_HANDLERS[handler_id] = handler
99
+ cmd.execute_with_handler_id(handler_id)
100
+ ctx.register_command(cmd, permission)
101
+
102
+ def schedule_delayed_task(
103
+ self, delay_ticks: int, handler: Callable[[server.Server], None]
104
+ ) -> int:
105
+ handler_id = _get_next_handler_id()
106
+ _TASK_HANDLERS[handler_id] = handler
107
+ return scheduler.schedule_delayed_task(handler_id, delay_ticks)
108
+
109
+ def schedule_repeating_task(
110
+ self,
111
+ delay_ticks: int,
112
+ period_ticks: int,
113
+ handler: Callable[[server.Server], None],
114
+ ) -> int:
115
+ handler_id = _get_next_handler_id()
116
+ _TASK_HANDLERS[handler_id] = handler
117
+ return scheduler.schedule_repeating_task(handler_id, delay_ticks, period_ticks)
118
+
119
+ def event_handler(
120
+ self,
121
+ event_type: event.EventType,
122
+ priority=event.EventPriority.NORMAL,
123
+ blocking=True,
124
+ ):
125
+ def decorator(func):
126
+ self._pending_events.append((event_type, func, priority, blocking))
127
+ return func
128
+
129
+ return decorator
130
+
131
+
132
+ def register_plugin(plugin_class: Type[Plugin]):
133
+ global _PLUGIN_INSTANCE
134
+ _PLUGIN_INSTANCE = plugin_class()
135
+
136
+
137
+ class WitWorldImpl(wit_world.WitWorld):
138
+ def init_plugin(self) -> None:
139
+ pass
140
+
141
+ def on_load(self, context: context.Context) -> None:
142
+ if _PLUGIN_INSTANCE:
143
+ try:
144
+ # In WIT, on-load returns result<_, string>
145
+ # componentize-py handles this by expecting None or raising Err
146
+ _PLUGIN_INSTANCE.on_load(context)
147
+ except Err as e:
148
+ raise Err(_exception_message(e))
149
+ except Exception as e:
150
+ raise Err(_format_exception(e))
151
+
152
+ def on_unload(self, context: context.Context) -> None:
153
+ if _PLUGIN_INSTANCE:
154
+ try:
155
+ _PLUGIN_INSTANCE.on_unload(context)
156
+ except Err as e:
157
+ raise Err(_exception_message(e))
158
+ except Exception as e:
159
+ raise Err(_format_exception(e))
160
+
161
+ def handle_event(
162
+ self, event_id: int, server: server.Server, event: event.Event
163
+ ) -> event.Event:
164
+ if event_id in _EVENT_HANDLERS:
165
+ # Pass the inner event data to the handler
166
+ result = _EVENT_HANDLERS[event_id](server, event.value)
167
+ # If the handler returns something, assume it's the updated event data
168
+ if result is not None:
169
+ event.value = result
170
+ return event
171
+
172
+ def handle_command(
173
+ self,
174
+ command_id: int,
175
+ sender: command.CommandSender,
176
+ server: server.Server,
177
+ args: command.ConsumedArgs,
178
+ ) -> int:
179
+ if command_id in _COMMAND_HANDLERS:
180
+ try:
181
+ return _COMMAND_HANDLERS[command_id](sender, server, args)
182
+ except Exception as e:
183
+ if hasattr(e, "value") and isinstance(
184
+ e.value,
185
+ (
186
+ command.CommandError_InvalidConsumption,
187
+ command.CommandError_InvalidRequirement,
188
+ command.CommandError_PermissionDenied,
189
+ command.CommandError_CommandFailed,
190
+ ),
191
+ ):
192
+ raise e
193
+ raise Err(
194
+ command.CommandError_CommandFailed(
195
+ text.TextComponent.text(_format_exception(e))
196
+ )
197
+ )
198
+
199
+ raise Err(
200
+ command.CommandError_CommandFailed(
201
+ text.TextComponent.text(
202
+ f"no handler registered for command id {command_id}"
203
+ )
204
+ )
205
+ )
206
+
207
+ def handle_task(self, handler_id: int, server: server.Server) -> None:
208
+ if handler_id in _TASK_HANDLERS:
209
+ _TASK_HANDLERS[handler_id](server)
210
+
211
+
212
+ class MetadataImpl:
213
+ def get_metadata(self) -> metadata.PluginMetadata:
214
+ if _PLUGIN_INSTANCE:
215
+ return _PLUGIN_INSTANCE.metadata()
216
+ return metadata.PluginMetadata("unknown", "0.0.0", [], "No metadata", [])
@@ -1,4 +1,4 @@
1
- interface entity {
1
+ interface block-entity {
2
2
  use common.{block-position};
3
3
 
4
4
  resource block-entity {
@@ -30,7 +30,7 @@ interface entity {
30
30
  powered: func() -> bool;
31
31
  }
32
32
 
33
- variant entity {
33
+ variant block-entity-type {
34
34
  command-block-entity(command-block-entity),
35
35
  }
36
36
  }
@@ -0,0 +1,82 @@
1
+ interface boss-bar {
2
+ use text.{text-component};
3
+ use player.{player};
4
+
5
+ /// Represents the color of a boss bar.
6
+ enum boss-bar-color {
7
+ pink,
8
+ blue,
9
+ red,
10
+ green,
11
+ yellow,
12
+ purple,
13
+ white,
14
+ }
15
+
16
+ /// Represents the notches/divisions of a boss bar.
17
+ enum boss-bar-division {
18
+ no-division,
19
+ notches-6,
20
+ notches-10,
21
+ notches-12,
22
+ notches-20,
23
+ }
24
+
25
+ /// Metadata flags that can be toggled on a boss bar.
26
+ record boss-bar-metadata {
27
+ /// Darkens the sky for players who see the boss bar.
28
+ darken-sky: bool,
29
+ /// If true, the bar is considered a "dragon bar" (used for Ender Dragon).
30
+ dragon-bar: bool,
31
+ /// If true, fog is created around the players who see the boss bar.
32
+ create-fog: bool,
33
+ }
34
+
35
+ /// Represents a boss bar that can be shown to players.
36
+ resource boss-bar {
37
+ /// Creates a new boss bar.
38
+ constructor(title: text-component, color: boss-bar-color, division: boss-bar-division);
39
+
40
+ /// Returns the title of this boss bar.
41
+ get-title: func() -> text-component;
42
+
43
+ /// Sets the title of this boss bar.
44
+ set-title: func(title: text-component);
45
+
46
+ /// Returns the health (0.0 to 1.0) of this boss bar.
47
+ get-health: func() -> f32;
48
+
49
+ /// Sets the health (0.0 to 1.0) of this boss bar.
50
+ set-health: func(health: f32);
51
+
52
+ /// Returns the color of this boss bar.
53
+ get-color: func() -> boss-bar-color;
54
+
55
+ /// Sets the color of this boss bar.
56
+ set-color: func(color: boss-bar-color);
57
+
58
+ /// Returns the division of this boss bar.
59
+ get-division: func() -> boss-bar-division;
60
+
61
+ /// Sets the division of this boss bar.
62
+ set-division: func(division: boss-bar-division);
63
+
64
+ /// Returns the currently active metadata on this boss bar.
65
+ get-metadata: func() -> boss-bar-metadata;
66
+
67
+ /// Updates the metadata on this boss bar.
68
+ set-metadata: func(metadata: boss-bar-metadata);
69
+
70
+ /// Adds a player to this boss bar, making it visible to them.
71
+ add-player: func(player: player);
72
+
73
+ /// Removes a player from this boss bar, hiding it from them.
74
+ remove-player: func(player: player);
75
+
76
+ /// Returns a list of all players currently seeing this boss bar.
77
+ get-players: func() -> list<player>;
78
+
79
+ /// Removes this boss bar from all players and cleans it up.
80
+ remove-all: func();
81
+ }
82
+ }
@@ -1,7 +1,8 @@
1
1
  interface command {
2
- use player.{player};
3
2
  use %world.{%world};
4
- use entity.{command-block-entity, entity};
3
+ use entity.{entity};
4
+ use player.{player};
5
+ use block-entity.{command-block-entity};
5
6
  use common.{position, block-position, locale};
6
7
  use server.{server, difficulty};
7
8
  use text.{text-component};
@@ -67,6 +67,28 @@ interface common {
67
67
  count: u8,
68
68
  }
69
69
 
70
+ /// Represents the various poses an entity can be in.
71
+ enum entity-pose {
72
+ standing,
73
+ fall-flying,
74
+ sleeping,
75
+ swimming,
76
+ spin-attack,
77
+ crouching,
78
+ long-jumping,
79
+ dying,
80
+ croaking,
81
+ using-tongue,
82
+ sitting,
83
+ roaring,
84
+ sniffing,
85
+ emerging,
86
+ digging,
87
+ sliding,
88
+ shooting,
89
+ inhaling,
90
+ }
91
+
70
92
  /// All Minecraft-supported locales.
71
93
  enum locale {
72
94
  af-za,
@@ -0,0 +1,3 @@
1
+ interface entity {
2
+ use %world.{entity};
3
+ }
@@ -150,7 +150,6 @@ interface event {
150
150
  player: player,
151
151
  action: interact-action,
152
152
  clicked-pos: option<block-position>,
153
- item: item-stack,
154
153
  block: string,
155
154
  cancelled: bool
156
155
  }
@@ -161,6 +160,12 @@ interface event {
161
160
  cancelled: bool
162
161
  }
163
162
 
163
+ record player-toggle-flight-event-data {
164
+ player: player,
165
+ is-flying: bool,
166
+ cancelled: bool
167
+ }
168
+
164
169
  record player-toggle-sprint-event-data {
165
170
  player: player,
166
171
  is-sprinting: bool,
@@ -265,6 +270,7 @@ interface event {
265
270
  player-interact-unknown-entity-event,
266
271
  player-interact-event,
267
272
  player-toggle-sneak-event,
273
+ player-toggle-flight-event,
268
274
  player-toggle-sprint-event,
269
275
  block-redstone-event,
270
276
  block-break-event,
@@ -297,6 +303,7 @@ interface event {
297
303
  player-interact-unknown-entity-event(player-interact-unknown-entity-event-data),
298
304
  player-interact-event(player-interact-event-data),
299
305
  player-toggle-sneak-event(player-toggle-sneak-event-data),
306
+ player-toggle-flight-event(player-toggle-flight-event-data),
300
307
  player-toggle-sprint-event(player-toggle-sprint-event-data),
301
308
  block-redstone-event(block-redstone-event-data),
302
309
  block-break-event(block-break-event-data),
@@ -34,5 +34,17 @@ interface gui {
34
34
  constructor(%type: gui-type, title: text-component);
35
35
  set-item: func(slot: u32, item: item-stack);
36
36
  get-item: func(slot: u32) -> option<item-stack>;
37
+
38
+ /// Returns the type of this GUI.
39
+ get-type: func() -> gui-type;
40
+
41
+ /// Returns the title of this GUI.
42
+ get-title: func() -> text-component;
43
+
44
+ /// Returns the number of slots in this GUI.
45
+ get-size: func() -> u32;
46
+
47
+ /// Clears all items from the GUI.
48
+ clear-items: func();
37
49
  }
38
50
  }