pumpkin-api-py 0.1.0.dev0__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 (75) hide show
  1. pumpkin_api/__init__.py +139 -0
  2. pumpkin_api/app.py +8 -0
  3. pumpkin_api/build.py +35 -0
  4. pumpkin_api/wit/componentize_py_async_support/__init__.py +529 -0
  5. pumpkin_api/wit/componentize_py_async_support/futures.py +93 -0
  6. pumpkin_api/wit/componentize_py_async_support/streams.py +189 -0
  7. pumpkin_api/wit/componentize_py_runtime.pyi +34 -0
  8. pumpkin_api/wit/componentize_py_types.py +19 -0
  9. pumpkin_api/wit/poll_loop.py +440 -0
  10. pumpkin_api/wit/wit_world/__init__.py +70 -0
  11. pumpkin_api/wit/wit_world/exports/__init__.py +19 -0
  12. pumpkin_api/wit/wit_world/exports/metadata.py +22 -0
  13. pumpkin_api/wit/wit_world/imports/__init__.py +0 -0
  14. pumpkin_api/wit/wit_world/imports/command.py +539 -0
  15. pumpkin_api/wit/wit_world/imports/common.py +213 -0
  16. pumpkin_api/wit/wit_world/imports/context.py +70 -0
  17. pumpkin_api/wit/wit_world/imports/entity.py +84 -0
  18. pumpkin_api/wit/wit_world/imports/event.py +434 -0
  19. pumpkin_api/wit/wit_world/imports/gui.py +61 -0
  20. pumpkin_api/wit/wit_world/imports/i18n.py +35 -0
  21. pumpkin_api/wit/wit_world/imports/logging.py +30 -0
  22. pumpkin_api/wit/wit_world/imports/permission.py +71 -0
  23. pumpkin_api/wit/wit_world/imports/player.py +350 -0
  24. pumpkin_api/wit/wit_world/imports/scheduler.py +44 -0
  25. pumpkin_api/wit/wit_world/imports/scoreboard.py +95 -0
  26. pumpkin_api/wit/wit_world/imports/server.py +75 -0
  27. pumpkin_api/wit/wit_world/imports/text.py +89 -0
  28. pumpkin_api/wit/wit_world/imports/world.py +215 -0
  29. pumpkin_api/wit_files/command.wit +189 -0
  30. pumpkin_api/wit_files/common.wit +201 -0
  31. pumpkin_api/wit_files/componentize_py_async_support/__init__.py +529 -0
  32. pumpkin_api/wit_files/componentize_py_async_support/futures.py +93 -0
  33. pumpkin_api/wit_files/componentize_py_async_support/streams.py +189 -0
  34. pumpkin_api/wit_files/componentize_py_runtime.pyi +34 -0
  35. pumpkin_api/wit_files/componentize_py_types.py +19 -0
  36. pumpkin_api/wit_files/context.wit +33 -0
  37. pumpkin_api/wit_files/entity.wit +36 -0
  38. pumpkin_api/wit_files/event.wit +311 -0
  39. pumpkin_api/wit_files/gui.wit +38 -0
  40. pumpkin_api/wit_files/i18n.wit +21 -0
  41. pumpkin_api/wit_files/log.wit +14 -0
  42. pumpkin_api/wit_files/metadata.wit +15 -0
  43. pumpkin_api/wit_files/permission.wit +46 -0
  44. pumpkin_api/wit_files/player.wit +172 -0
  45. pumpkin_api/wit_files/plugin.wit +61 -0
  46. pumpkin_api/wit_files/poll_loop.py +440 -0
  47. pumpkin_api/wit_files/scheduler.wit +24 -0
  48. pumpkin_api/wit_files/scoreboard.wit +72 -0
  49. pumpkin_api/wit_files/server.wit +41 -0
  50. pumpkin_api/wit_files/text.wit +41 -0
  51. pumpkin_api/wit_files/wit_world/__init__.py +70 -0
  52. pumpkin_api/wit_files/wit_world/exports/__init__.py +19 -0
  53. pumpkin_api/wit_files/wit_world/exports/metadata.py +22 -0
  54. pumpkin_api/wit_files/wit_world/imports/__init__.py +0 -0
  55. pumpkin_api/wit_files/wit_world/imports/command.py +539 -0
  56. pumpkin_api/wit_files/wit_world/imports/common.py +213 -0
  57. pumpkin_api/wit_files/wit_world/imports/context.py +70 -0
  58. pumpkin_api/wit_files/wit_world/imports/entity.py +84 -0
  59. pumpkin_api/wit_files/wit_world/imports/event.py +434 -0
  60. pumpkin_api/wit_files/wit_world/imports/gui.py +61 -0
  61. pumpkin_api/wit_files/wit_world/imports/i18n.py +35 -0
  62. pumpkin_api/wit_files/wit_world/imports/logging.py +30 -0
  63. pumpkin_api/wit_files/wit_world/imports/permission.py +71 -0
  64. pumpkin_api/wit_files/wit_world/imports/player.py +350 -0
  65. pumpkin_api/wit_files/wit_world/imports/scheduler.py +44 -0
  66. pumpkin_api/wit_files/wit_world/imports/scoreboard.py +95 -0
  67. pumpkin_api/wit_files/wit_world/imports/server.py +75 -0
  68. pumpkin_api/wit_files/wit_world/imports/text.py +89 -0
  69. pumpkin_api/wit_files/wit_world/imports/world.py +215 -0
  70. pumpkin_api/wit_files/world.wit +169 -0
  71. pumpkin_api_py-0.1.0.dev0.dist-info/METADATA +58 -0
  72. pumpkin_api_py-0.1.0.dev0.dist-info/RECORD +75 -0
  73. pumpkin_api_py-0.1.0.dev0.dist-info/WHEEL +4 -0
  74. pumpkin_api_py-0.1.0.dev0.dist-info/entry_points.txt +2 -0
  75. pumpkin_api_py-0.1.0.dev0.dist-info/licenses/LICENSE +677 -0
@@ -0,0 +1,139 @@
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
27
+ )
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
+
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")
pumpkin_api/app.py ADDED
@@ -0,0 +1,8 @@
1
+ from . import WitWorldImpl, MetadataImpl
2
+ from .wit import wit_world
3
+
4
+ class WitWorld(WitWorldImpl, wit_world.WitWorld):
5
+ pass
6
+
7
+ class Metadata(MetadataImpl, wit_world.exports.Metadata):
8
+ pass
pumpkin_api/build.py ADDED
@@ -0,0 +1,35 @@
1
+ import argparse
2
+ import subprocess
3
+ import sys
4
+ import os
5
+
6
+ def main():
7
+ 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")
10
+ args = parser.parse_args()
11
+
12
+ import pumpkin_api
13
+ pkg_dir = os.path.dirname(pumpkin_api.__file__)
14
+ wit_dir = os.path.join(pkg_dir, "wit_files")
15
+
16
+ # Pass the parent directory of pumpkin_api to componentize-py so it can resolve `pumpkin_api` module
17
+ # in case it's not installed in a standard site-packages (e.g. editable install or no venv)
18
+ pkg_parent_dir = os.path.dirname(pkg_dir)
19
+
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)
33
+
34
+ if __name__ == "__main__":
35
+ main()