pumpkin-api-py 0.1.0__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 (108) hide show
  1. componentize_py_async_support/__init__.py +570 -0
  2. componentize_py_async_support/futures.py +149 -0
  3. componentize_py_async_support/streams.py +345 -0
  4. componentize_py_runtime.pyi +40 -0
  5. componentize_py_types.py +32 -0
  6. poll_loop.py +440 -0
  7. pumpkin_api/__init__.py +41 -0
  8. pumpkin_api/build.py +68 -0
  9. pumpkin_api/impl.py +355 -0
  10. pumpkin_api/wit/advancement.wit +41 -0
  11. pumpkin_api/wit/attributes.wit +56 -0
  12. pumpkin_api/wit/bedrock-packets.wit +1709 -0
  13. pumpkin_api/wit/biomes.wit +72 -0
  14. pumpkin_api/wit/block-entity.wit +423 -0
  15. pumpkin_api/wit/boss-bar.wit +83 -0
  16. pumpkin_api/wit/command.wit +244 -0
  17. pumpkin_api/wit/common.wit +261 -0
  18. pumpkin_api/wit/context.wit +61 -0
  19. pumpkin_api/wit/damage-types.wit +57 -0
  20. pumpkin_api/wit/data-components.wit +117 -0
  21. pumpkin_api/wit/datapack.wit +54 -0
  22. pumpkin_api/wit/display.wit +171 -0
  23. pumpkin_api/wit/enchantments.wit +102 -0
  24. pumpkin_api/wit/entity-statuses.wit +68 -0
  25. pumpkin_api/wit/entity-types.wit +164 -0
  26. pumpkin_api/wit/entity.wit +1 -0
  27. pumpkin_api/wit/event.wit +2435 -0
  28. pumpkin_api/wit/forms.wit +51 -0
  29. pumpkin_api/wit/game-events.wit +67 -0
  30. pumpkin_api/wit/game-rules.wit +69 -0
  31. pumpkin_api/wit/gui.wit +39 -0
  32. pumpkin_api/wit/i18n.wit +21 -0
  33. pumpkin_api/wit/inventory.wit +78 -0
  34. pumpkin_api/wit/ipc.wit +7 -0
  35. pumpkin_api/wit/item-stack.wit +104 -0
  36. pumpkin_api/wit/java-dialogs.wit +155 -0
  37. pumpkin_api/wit/java-packets.wit +1838 -0
  38. pumpkin_api/wit/log.wit +14 -0
  39. pumpkin_api/wit/metadata.wit +19 -0
  40. pumpkin_api/wit/particles.wit +131 -0
  41. pumpkin_api/wit/permission.wit +46 -0
  42. pumpkin_api/wit/player.wit +1432 -0
  43. pumpkin_api/wit/plugin.wit +121 -0
  44. pumpkin_api/wit/potions.wit +52 -0
  45. pumpkin_api/wit/recipe.wit +88 -0
  46. pumpkin_api/wit/scheduler.wit +24 -0
  47. pumpkin_api/wit/scoreboard.wit +111 -0
  48. pumpkin_api/wit/screens.wit +31 -0
  49. pumpkin_api/wit/server.wit +286 -0
  50. pumpkin_api/wit/sounds.wit +1987 -0
  51. pumpkin_api/wit/statistics.wit +94 -0
  52. pumpkin_api/wit/status-effect.wit +54 -0
  53. pumpkin_api/wit/text.wit +68 -0
  54. pumpkin_api/wit/uuid.wit +16 -0
  55. pumpkin_api/wit/world.wit +1050 -0
  56. pumpkin_api_py-0.1.0.dist-info/METADATA +53 -0
  57. pumpkin_api_py-0.1.0.dist-info/RECORD +108 -0
  58. pumpkin_api_py-0.1.0.dist-info/WHEEL +4 -0
  59. pumpkin_api_py-0.1.0.dist-info/entry_points.txt +2 -0
  60. pumpkin_api_py-0.1.0.dist-info/licenses/LICENSE-APACHE +176 -0
  61. pumpkin_api_py-0.1.0.dist-info/licenses/LICENSE-MIT +21 -0
  62. wit_world/__init__.py +114 -0
  63. wit_world/exports/__init__.py +19 -0
  64. wit_world/exports/metadata.py +24 -0
  65. wit_world/imports/__init__.py +0 -0
  66. wit_world/imports/advancement.py +56 -0
  67. wit_world/imports/attributes.py +66 -0
  68. wit_world/imports/bedrock_packets.py +2413 -0
  69. wit_world/imports/biomes.py +81 -0
  70. wit_world/imports/block_entity.py +1353 -0
  71. wit_world/imports/boss_bar.py +137 -0
  72. wit_world/imports/command.py +705 -0
  73. wit_world/imports/common.py +335 -0
  74. wit_world/imports/context.py +91 -0
  75. wit_world/imports/damage_types.py +66 -0
  76. wit_world/imports/data_components.py +126 -0
  77. wit_world/imports/datapack.py +124 -0
  78. wit_world/imports/display.py +295 -0
  79. wit_world/imports/enchantments.py +129 -0
  80. wit_world/imports/entity_statuses.py +77 -0
  81. wit_world/imports/entity_types.py +173 -0
  82. wit_world/imports/event.py +3558 -0
  83. wit_world/imports/forms.py +98 -0
  84. wit_world/imports/game_events.py +76 -0
  85. wit_world/imports/game_rules.py +88 -0
  86. wit_world/imports/gui.py +81 -0
  87. wit_world/imports/i18n.py +35 -0
  88. wit_world/imports/inventory.py +159 -0
  89. wit_world/imports/ipc.py +20 -0
  90. wit_world/imports/item_stack.py +206 -0
  91. wit_world/imports/java_dialogs.py +208 -0
  92. wit_world/imports/java_packets.py +3560 -0
  93. wit_world/imports/logging.py +30 -0
  94. wit_world/imports/particles.py +140 -0
  95. wit_world/imports/permission.py +71 -0
  96. wit_world/imports/player.py +1753 -0
  97. wit_world/imports/potions.py +61 -0
  98. wit_world/imports/recipe.py +120 -0
  99. wit_world/imports/scheduler.py +44 -0
  100. wit_world/imports/scoreboard.py +167 -0
  101. wit_world/imports/screens.py +40 -0
  102. wit_world/imports/server.py +493 -0
  103. wit_world/imports/sounds.py +1996 -0
  104. wit_world/imports/statistics.py +103 -0
  105. wit_world/imports/status_effect.py +63 -0
  106. wit_world/imports/text.py +156 -0
  107. wit_world/imports/uuid.py +36 -0
  108. wit_world/imports/world.py +1640 -0
pumpkin_api/impl.py ADDED
@@ -0,0 +1,355 @@
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, TypedDict
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, world
25
+
26
+
27
+ class AiGoal(TypedDict):
28
+ can_start: Callable[[server.Server, world.Entity], bool]
29
+ should_continue: Callable[[server.Server, world.Entity], bool]
30
+ start: Callable[[server.Server, world.Entity], None]
31
+ tick: Callable[[server.Server, world.Entity], None]
32
+ stop: Callable[[server.Server, world.Entity], None]
33
+
34
+
35
+ _PLUGIN_INSTANCE: Optional["Plugin"] = None
36
+ _EVENT_HANDLERS: Dict[int, Callable[[server.Server, Any], Any]] = {}
37
+ _COMMAND_HANDLERS: Dict[
38
+ int, Callable[[command.CommandSender, server.Server, command.ConsumedArgs], int]
39
+ ] = {}
40
+ _COMMAND_SUGGESTION_HANDLERS: Dict[
41
+ int,
42
+ Callable[
43
+ [command.CommandSender, server.Server, command.SuggestionRequest],
44
+ command.CommandSuggestions,
45
+ ],
46
+ ] = {}
47
+ _TASK_HANDLERS: Dict[int, Callable[[server.Server], None]] = {}
48
+ _AI_GOALS: Dict[int, AiGoal] = {}
49
+ _GENERATORS: Dict[int, Callable[[world.GenerationPhase, world.ChunkBuffer], None]] = {}
50
+
51
+ _NEXT_HANDLER_ID: int = 0
52
+
53
+
54
+ def _exception_message(exc: Exception) -> str:
55
+ if hasattr(exc, "value"):
56
+ value = getattr(exc, "value")
57
+ if value is not None:
58
+ return str(value)
59
+ message = str(exc)
60
+ if message:
61
+ return message
62
+ return "".join(traceback.format_exception_only(type(exc), exc)).strip()
63
+
64
+
65
+ def _format_exception(exc: Exception) -> str:
66
+ details = "".join(
67
+ traceback.format_exception(type(exc), exc, exc.__traceback__)
68
+ ).strip()
69
+ if details:
70
+ return details
71
+ return _exception_message(exc)
72
+
73
+
74
+ def _get_next_handler_id() -> int:
75
+ global _NEXT_HANDLER_ID
76
+ handler_id = _NEXT_HANDLER_ID
77
+ _NEXT_HANDLER_ID += 1
78
+ return handler_id
79
+
80
+
81
+ class Plugin:
82
+ def __init__(self):
83
+ self._pending_events = []
84
+
85
+ def metadata(self) -> metadata.PluginMetadata:
86
+ raise NotImplementedError
87
+
88
+ def on_load(self, ctx: context.Context) -> None:
89
+ for event_type, handler, priority, blocking in self._pending_events:
90
+ self.register_event(ctx, event_type, handler, priority, blocking)
91
+
92
+ def on_unload(self, ctx: context.Context) -> None:
93
+ pass
94
+
95
+ def register_event(
96
+ self,
97
+ ctx: context.Context,
98
+ event_type: event.EventType,
99
+ handler: Callable[[server.Server, Any], Any],
100
+ priority=event.EventPriority.NORMAL,
101
+ blocking=True,
102
+ ):
103
+ handler_id = _get_next_handler_id()
104
+ _EVENT_HANDLERS[handler_id] = handler
105
+ ctx.register_event(handler_id, event_type, priority, blocking)
106
+
107
+ def register_command_handler(
108
+ self,
109
+ handler: Callable[
110
+ [command.CommandSender, server.Server, command.ConsumedArgs], int
111
+ ],
112
+ ) -> int:
113
+ handler_id = _get_next_handler_id()
114
+ _COMMAND_HANDLERS[handler_id] = handler
115
+ return handler_id
116
+
117
+ def register_command_suggestion_handler(
118
+ self,
119
+ handler: Callable[
120
+ [command.CommandSender, server.Server, command.SuggestionRequest],
121
+ command.CommandSuggestions,
122
+ ],
123
+ ) -> int:
124
+ handler_id = _get_next_handler_id()
125
+ _COMMAND_SUGGESTION_HANDLERS[handler_id] = handler
126
+ return handler_id
127
+
128
+ def register_command(
129
+ self,
130
+ ctx: context.Context,
131
+ cmd: command.Command,
132
+ handler: Callable[
133
+ [command.CommandSender, server.Server, command.ConsumedArgs], int
134
+ ],
135
+ suggestion_handler: Callable[
136
+ [command.CommandSender, server.Server, command.SuggestionRequest],
137
+ command.CommandSuggestions,
138
+ ]
139
+ | None = None,
140
+ permission: str = "",
141
+ extra_nodes: list[command.CommandNode] | None = None,
142
+ ):
143
+ suggestion_id = None
144
+ if suggestion_handler:
145
+ suggestion_id = self.register_command_suggestion_handler(suggestion_handler)
146
+ handler_id = self.register_command_handler(handler)
147
+ cmd.execute_with_handler_id(handler_id)
148
+ if extra_nodes:
149
+ for node in extra_nodes:
150
+ node.execute_with_handler_id(handler_id)
151
+ if suggestion_id:
152
+ node.suggest_with_handler_id(suggestion_id)
153
+ ctx.register_command(cmd, permission)
154
+
155
+ def register_ai_goal(self, goal: AiGoal) -> int:
156
+ goal_id = _get_next_handler_id()
157
+ _AI_GOALS[goal_id] = goal
158
+ return goal_id
159
+
160
+ def register_chunk_generator(
161
+ self, generator: Callable[[world.GenerationPhase, world.ChunkBuffer], None]
162
+ ) -> int:
163
+ generator_id = _get_next_handler_id()
164
+ _GENERATORS[generator_id] = generator
165
+ return generator_id
166
+
167
+ def schedule_delayed_task(
168
+ self, delay_ticks: int, handler: Callable[[server.Server], None]
169
+ ) -> int:
170
+ handler_id = _get_next_handler_id()
171
+ _TASK_HANDLERS[handler_id] = handler
172
+ return scheduler.schedule_delayed_task(handler_id, delay_ticks)
173
+
174
+ def schedule_repeating_task(
175
+ self,
176
+ delay_ticks: int,
177
+ period_ticks: int,
178
+ handler: Callable[[server.Server], None],
179
+ ) -> int:
180
+ handler_id = _get_next_handler_id()
181
+ _TASK_HANDLERS[handler_id] = handler
182
+ return scheduler.schedule_repeating_task(handler_id, delay_ticks, period_ticks)
183
+
184
+ def event_handler(
185
+ self,
186
+ event_type: event.EventType,
187
+ priority=event.EventPriority.NORMAL,
188
+ blocking=True,
189
+ ):
190
+ def decorator(func):
191
+ self._pending_events.append((event_type, func, priority, blocking))
192
+ return func
193
+
194
+ return decorator
195
+
196
+ def handle_ipc_message(self, sender: str, message: bytes) -> bytes:
197
+ raise NotImplementedError
198
+
199
+
200
+ def register_plugin(plugin_class: Type[Plugin]):
201
+ global _PLUGIN_INSTANCE
202
+ _PLUGIN_INSTANCE = plugin_class()
203
+
204
+
205
+ class WitWorldImpl(wit_world.WitWorld):
206
+ def init_plugin(self) -> None:
207
+ pass
208
+
209
+ def on_load(self, context: context.Context) -> None:
210
+ if _PLUGIN_INSTANCE:
211
+ try:
212
+ # In WIT, on-load returns result<_, string>
213
+ # componentize-py handles this by expecting None or raising Err
214
+ _PLUGIN_INSTANCE.on_load(context)
215
+ except Err as e:
216
+ raise Err(_exception_message(e))
217
+ except Exception as e:
218
+ raise Err(_format_exception(e))
219
+
220
+ def on_unload(self, context: context.Context) -> None:
221
+ if _PLUGIN_INSTANCE:
222
+ try:
223
+ _PLUGIN_INSTANCE.on_unload(context)
224
+ except Err as e:
225
+ raise Err(_exception_message(e))
226
+ except Exception as e:
227
+ raise Err(_format_exception(e))
228
+
229
+ def handle_event(
230
+ self, event_id: int, server: server.Server, event: event.Event
231
+ ) -> event.Event:
232
+ if event_id in _EVENT_HANDLERS:
233
+ # Pass the inner event data to the handler
234
+ result = _EVENT_HANDLERS[event_id](server, event.value)
235
+ # If the handler returns something, assume it's the updated event data
236
+ if result is not None:
237
+ event.value = result
238
+ return event
239
+
240
+ def handle_command(
241
+ self,
242
+ command_id: int,
243
+ sender: command.CommandSender,
244
+ server: server.Server,
245
+ args: command.ConsumedArgs,
246
+ ) -> int:
247
+ if command_id in _COMMAND_HANDLERS:
248
+ try:
249
+ return _COMMAND_HANDLERS[command_id](sender, server, args)
250
+ except Exception as e:
251
+ if hasattr(e, "value") and isinstance(
252
+ e.value,
253
+ (
254
+ command.CommandError_InvalidConsumption,
255
+ command.CommandError_InvalidRequirement,
256
+ command.CommandError_PermissionDenied,
257
+ command.CommandError_CommandFailed,
258
+ ),
259
+ ):
260
+ raise e
261
+ raise Err(
262
+ command.CommandError_CommandFailed(
263
+ text.TextComponent.text(_format_exception(e))
264
+ )
265
+ )
266
+
267
+ raise Err(
268
+ command.CommandError_CommandFailed(
269
+ text.TextComponent.text(
270
+ f"no handler registered for command id {command_id}"
271
+ )
272
+ )
273
+ )
274
+
275
+ def handle_command_suggestion(
276
+ self,
277
+ handler_id: int,
278
+ sender: command.CommandSender,
279
+ server: server.Server,
280
+ request: command.SuggestionRequest,
281
+ ) -> command.CommandSuggestions:
282
+ if handler_id in _COMMAND_SUGGESTION_HANDLERS:
283
+ return _COMMAND_SUGGESTION_HANDLERS[handler_id](sender, server, request)
284
+
285
+ raise Exception(f"No suggestion handler registered for ID {handler_id}")
286
+
287
+ def handle_task(self, handler_id: int, server: server.Server) -> None:
288
+ if handler_id in _TASK_HANDLERS:
289
+ _TASK_HANDLERS[handler_id](server)
290
+
291
+ def handle_ipc_message(self, sender: str, message: bytes) -> bytes:
292
+ if _PLUGIN_INSTANCE:
293
+ try:
294
+ return _PLUGIN_INSTANCE.handle_ipc_message(sender, message)
295
+ except Err as e:
296
+ raise Err(_exception_message(e))
297
+ except Exception as e:
298
+ raise Err(_format_exception(e))
299
+
300
+ raise Err("Internal error: No plugin instance available?")
301
+
302
+ def handle_ai_goal_can_start(
303
+ self, goal_id: int, server: server.Server, entity: world.Entity
304
+ ) -> bool:
305
+ if goal_id in _AI_GOALS:
306
+ return _AI_GOALS[goal_id]["can_start"](server, entity)
307
+
308
+ raise Exception(f"No AI goal registered for ID {goal_id}")
309
+
310
+ def handle_ai_goal_should_continue(
311
+ self, goal_id: int, server: server.Server, entity: world.Entity
312
+ ) -> bool:
313
+ if goal_id in _AI_GOALS:
314
+ return _AI_GOALS[goal_id]["should_continue"](server, entity)
315
+
316
+ raise Exception(f"No AI goal registered for ID {goal_id}")
317
+
318
+ def handle_ai_goal_start(
319
+ self, goal_id: int, server: server.Server, entity: world.Entity
320
+ ) -> None:
321
+ if goal_id in _AI_GOALS:
322
+ _AI_GOALS[goal_id]["start"](server, entity)
323
+ return
324
+ raise Exception(f"No AI goal registered for ID {goal_id}")
325
+
326
+ def handle_ai_goal_tick(
327
+ self, goal_id: int, server: server.Server, entity: world.Entity
328
+ ) -> None:
329
+ if goal_id in _AI_GOALS:
330
+ _AI_GOALS[goal_id]["tick"](server, entity)
331
+ return
332
+ raise Exception(f"No AI goal registered for ID {goal_id}")
333
+
334
+ def handle_ai_goal_stop(
335
+ self, goal_id: int, server: server.Server, entity: world.Entity
336
+ ) -> None:
337
+ if goal_id in _AI_GOALS:
338
+ _AI_GOALS[goal_id]["stop"](server, entity)
339
+ return
340
+ raise Exception(f"No AI goal registered for ID {goal_id}")
341
+
342
+ def handle_generate_phase(
343
+ self, generator_id: int, phase: world.GenerationPhase, chunk: world.ChunkBuffer
344
+ ) -> None:
345
+ if generator_id in _AI_GOALS:
346
+ _GENERATORS[generator_id](phase, chunk)
347
+ return
348
+ raise Exception(f"No generator registered for ID {generator_id}")
349
+
350
+
351
+ class MetadataImpl:
352
+ def get_metadata(self) -> metadata.PluginMetadata:
353
+ if _PLUGIN_INSTANCE:
354
+ return _PLUGIN_INSTANCE.metadata()
355
+ return metadata.PluginMetadata("unknown", "0.0.0", [], "No metadata", [], [])
@@ -0,0 +1,41 @@
1
+ package pumpkin:plugin@0.1.0;
2
+
3
+ interface advancement {
4
+ use text.{text-component};
5
+
6
+ /// Frame type for an advancement.
7
+ enum frame-type {
8
+ task,
9
+ challenge,
10
+ goal,
11
+ }
12
+
13
+ /// Display information for an advancement.
14
+ record advancement-display {
15
+ title: text-component,
16
+ description: text-component,
17
+ frame: frame-type,
18
+ show-toast: bool,
19
+ hidden: bool,
20
+ announce-to-chat: bool,
21
+ background: option<string>,
22
+ x: f32,
23
+ y: f32,
24
+ }
25
+
26
+ /// Detailed information about an advancement.
27
+ record advancement-info {
28
+ id: string,
29
+ parent-id: option<string>,
30
+ criteria: list<string>,
31
+ display: option<advancement-display>,
32
+ }
33
+
34
+ /// Represents progress of an advancement for a player.
35
+ record advancement-progress {
36
+ advancement-id: string,
37
+ done: bool,
38
+ awarded-criteria: list<string>,
39
+ remaining-criteria: list<string>,
40
+ }
41
+ }
@@ -0,0 +1,56 @@
1
+ package pumpkin:plugin@0.1.0;
2
+
3
+ interface attributes {
4
+ enum attribute {
5
+ air-drag-modifier,
6
+ armor,
7
+ armor-toughness,
8
+ attack-damage,
9
+ attack-knockback,
10
+ attack-speed,
11
+ below-name-distance,
12
+ block-break-speed,
13
+ block-interaction-range,
14
+ bounciness,
15
+ burning-time,
16
+ camera-distance,
17
+ explosion-knockback-resistance,
18
+ entity-interaction-range,
19
+ fall-damage-multiplier,
20
+ flying-speed,
21
+ follow-range,
22
+ friction-modifier,
23
+ gravity,
24
+ jump-strength,
25
+ knockback-resistance,
26
+ luck,
27
+ max-absorption,
28
+ max-health,
29
+ mining-efficiency,
30
+ movement-efficiency,
31
+ movement-speed,
32
+ name-tag-distance,
33
+ oxygen-bonus,
34
+ safe-fall-distance,
35
+ scale,
36
+ sneaking-speed,
37
+ spawn-reinforcements,
38
+ step-height,
39
+ submerged-mining-speed,
40
+ sweeping-damage-ratio,
41
+ tempt-range,
42
+ water-movement-efficiency,
43
+ waypoint-transmit-range,
44
+ waypoint-receive-range,
45
+ }
46
+ enum modifier-operation {
47
+ add,
48
+ multiply-base,
49
+ multiply-total,
50
+ }
51
+ record attribute-modifier {
52
+ id: string,
53
+ amount: f64,
54
+ operation: modifier-operation,
55
+ }
56
+ }