st-minecraft 1.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.
@@ -0,0 +1,3 @@
1
+ __version__ = "1.1.0"
2
+ __author__ = "Chris Geron"
3
+ __email__ = "git@chris-ge.de"
@@ -0,0 +1 @@
1
+ """ st_minecraft package """
@@ -0,0 +1,3 @@
1
+ """ st_minecraft package """
2
+
3
+ from st_minecraft.core.core import * # noqa: unused-import
@@ -0,0 +1,126 @@
1
+ """
2
+ Core of the library, here communication with the server happens
3
+ this file also provides some helper functions
4
+ """
5
+
6
+ import os
7
+ import socket
8
+ from enum import Enum
9
+ from typing import Any
10
+ from typing import Optional
11
+ from typing import Type
12
+ from typing import TypeVar
13
+
14
+ # Global variable for the connection
15
+ connection: Optional[socket.socket] = None
16
+
17
+ ARG_SEPARATOR = "𝇉"
18
+
19
+ DEFAULT_PORT = 25595
20
+
21
+
22
+ def connect(ip: str = "0.0.0.0", port: int = DEFAULT_PORT) -> None:
23
+ """
24
+ Establishes a connection to the Minecraft server.
25
+
26
+ Args:
27
+ ip (str): The IP address of the server (default is "0.0.0.0" - equivalent to "localhost")
28
+ port (int): The port of the server.
29
+ """
30
+ # needed internally
31
+ global connection
32
+ if connection is not None:
33
+ connection.close()
34
+
35
+ # this allows us to develop more easily without having to adjust everything temporarily each time
36
+ _ip = os.getenv("SK_SERVER_OVERWRITE")
37
+ if _ip is not None:
38
+ spacer = "#" * 100
39
+ print(
40
+ f"{spacer}\n"
41
+ f"IP was overwritten by environment variable from '{ip}' to '{_ip}', "
42
+ f"(env var name: 'SK_SERVER_OVERWRITE')\n"
43
+ f"{spacer}"
44
+ )
45
+ ip = _ip
46
+
47
+ connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
48
+ connection.connect((ip, port))
49
+
50
+
51
+ def _receive(timeout: float = 2.0) -> bytes | None:
52
+ # needed internally
53
+
54
+ # this allows us to develop more easily without having to adjust everything temporarily each time
55
+ _timeout = os.getenv("SK_TIMEOUT_OVERWRITE")
56
+ if _timeout is not None:
57
+ if _timeout == "None":
58
+ _timeout = None
59
+ else:
60
+ _timeout = float(_timeout)
61
+
62
+ spacer = "#" * 100
63
+ print(
64
+ f"{spacer}\n"
65
+ f"Timeout was overwritten by environment variable from '{timeout}' to '{_timeout}', "
66
+ f"(env var name: 'SK_TIMEOUT_OVERWRITE')\n"
67
+ f"{spacer}"
68
+ )
69
+ timeout = _timeout
70
+
71
+ if timeout:
72
+ connection.settimeout(timeout) # Timeout in seconds
73
+
74
+ try:
75
+ data = connection.recv(1024)
76
+ except socket.timeout:
77
+ raise NoDataError(f"Timeout: After {timeout} seconds no response was received from the server.")
78
+
79
+ return data
80
+
81
+
82
+ def _build_command(*args: Any) -> str:
83
+ """
84
+ Takes all arguments, converts them to strings and builds the finished command from them
85
+ """
86
+ return ARG_SEPARATOR.join(map(str, args))
87
+
88
+
89
+ def _bytes_to_text(b: bytes) -> str:
90
+ # needed internally
91
+ # bytes to text :)
92
+ return b.decode("utf-8").strip()
93
+
94
+
95
+ def _send_command(command: str) -> None:
96
+ """
97
+ Sends a command over the global connection.
98
+
99
+ Args:
100
+ command (str): The command to send.
101
+ """
102
+ # needed internally
103
+ if connection is None:
104
+ raise RuntimeError("No connection to server. Please connect first.")
105
+
106
+ command = f"{command}\n"
107
+ connection.sendall(command.encode("utf-8"))
108
+
109
+
110
+ E = TypeVar("E", bound=Enum)
111
+
112
+
113
+ def _to_enum(enum: Type[E], value: Any) -> Optional[E]:
114
+ return enum._value2member_map_.get(value)
115
+
116
+
117
+ class NoDataError(ValueError):
118
+ """Raised when we receive nothing from the API"""
119
+
120
+
121
+ class WertFehler(ValueError):
122
+ """ValueError aber deutsch :clown face:"""
123
+
124
+
125
+ class InventoryFieldEmptyError(KeyError):
126
+ """Raised when trying to access an inventory field that is empty."""
@@ -0,0 +1,14 @@
1
+ from st_minecraft.core import connect as verbinden # noqa: unused-import
2
+ from st_minecraft.de.boss_leiste import * # noqa: unused-import
3
+ from st_minecraft.de.daten_modelle import * # noqa: unused-import
4
+ from st_minecraft.de.main import * # noqa: unused-import
5
+
6
+ try:
7
+ from st_minecraft.de.entity import * # noqa: unused-import
8
+ from st_minecraft.de.material import * # noqa: unused-import
9
+
10
+ except ModuleNotFoundError as e:
11
+ raise RuntimeError(
12
+ f"Es ist nicht möglich die auto-generierten Enums für Material und/oder Entity zu importieren."
13
+ f"Bitte überprüfe, ob diese vorliegen, falls nicht siehe bitte ressourcen/generate_enums.py im git repo."
14
+ ) from e
@@ -0,0 +1,134 @@
1
+ """Ermöglicht das erstellen und konfigurieren von Bossleisten"""
2
+
3
+ from enum import Enum
4
+
5
+ from pydantic import BaseModel
6
+
7
+ import st_minecraft.en as __st_minecraft_en
8
+ import st_minecraft.en.boss_bar as _st_minecraft_en_boss_bar
9
+ from st_minecraft.core.core import WertFehler
10
+
11
+
12
+ class BossLeisteStil(Enum):
13
+ """Möglichkeiten in denen der Stil einer Bossleiste angezeigt werden kann"""
14
+
15
+ DURCHGEZOGEN = "solid"
16
+ SEGMENTE_6 = "segmented_6"
17
+ SEGMENTE_10 = "segmented_10"
18
+ SEGMENTE_12 = "segmented_12"
19
+ SEGMENTE_20 = "segmented_20"
20
+
21
+ @staticmethod
22
+ def von_englisch(boss_bar_stil: _st_minecraft_en_boss_bar.BossBarStyle) -> "BossLeisteStil":
23
+ return BossLeisteStil._value2member_map_[boss_bar_stil.value]
24
+
25
+ def zu_englisch(self) -> _st_minecraft_en_boss_bar.BossBarStyle:
26
+ return _st_minecraft_en_boss_bar.BossBarStyle._value2member_map_[self.value]
27
+
28
+
29
+ class BossLeisteFarben(Enum):
30
+ """Farben in denen eine Bossleiste angezeigt werden kann"""
31
+
32
+ BLAU = "blue"
33
+ GRÜN = "green"
34
+ PINK = "pink"
35
+ LILA = "purple"
36
+ ROT = "red"
37
+ WEIß = "white"
38
+ GELB = "yellow"
39
+
40
+ @staticmethod
41
+ def von_englisch(boss_bar_farbe: _st_minecraft_en_boss_bar.BossBarColor) -> "BossLeisteFarben":
42
+ return BossLeisteFarben._value2member_map_[boss_bar_farbe.value]
43
+
44
+ def zu_englisch(self) -> _st_minecraft_en_boss_bar.BossBarColor:
45
+ return _st_minecraft_en_boss_bar.BossBarColor._value2member_map_[self.value]
46
+
47
+
48
+ class BossLeiste(BaseModel):
49
+ """Modell einer Bossleiste"""
50
+
51
+ name: str
52
+ """ Der von dir für die Leiste gesetze Name """
53
+ anzeige_text: str
54
+ """ Der Text der auf der Leiste angezeigt wird """
55
+ wert: float # zwischen 0 und 1
56
+ """ Wie viel von der Leiste gefüllt sein soll (zwischen 0 und 1) """
57
+ stil: BossLeisteStil
58
+ """ Anzeige Stil der Leiste (siehe BossLeisteStil) """
59
+ farbe: BossLeisteFarben
60
+ """ Anzeige Farbe der Leiste (siehe BossLeisteFarbe) """
61
+
62
+ def __repr__(self):
63
+ return (
64
+ f"BossLeiste(name={self.name}, "
65
+ f"anzeige_text={self.anzeige_text}, "
66
+ f"wert={self.wert:.2f}, "
67
+ f"stil={self.stil}, "
68
+ f"farbe={self.farbe})"
69
+ )
70
+
71
+ @staticmethod
72
+ def von_englisch(boss_bar: _st_minecraft_en_boss_bar.BossBar) -> "BossLeiste":
73
+ return BossLeiste(
74
+ name=boss_bar.name,
75
+ anzeige_text=boss_bar.display_text,
76
+ wert=boss_bar.value,
77
+ stil=BossLeisteStil.von_englisch(boss_bar.style),
78
+ farbe=BossLeisteFarben.von_englisch(boss_bar.color),
79
+ )
80
+
81
+ def zu_englisch(self) -> _st_minecraft_en_boss_bar.BossBar:
82
+ return _st_minecraft_en_boss_bar.BossBar(
83
+ name=self.name,
84
+ display_text=self.anzeige_text,
85
+ value=self.wert,
86
+ style=self.stil.zu_englisch(),
87
+ color=self.farbe.zu_englisch(),
88
+ )
89
+
90
+
91
+ def erzeuge_leiste(name: str, anzeige_text: str) -> BossLeiste:
92
+ """
93
+ Erstelle eine Bossleiste
94
+ Args:
95
+ name: Ein von dir gewählter Name, wie die Bossleiste für dich heißen soll
96
+ anzeige_text: Text der an der Leiste angezeigt werden soll
97
+
98
+ Returns:
99
+ Ein BossLeisten Objekt mit dessen Hilfe du die Leiste weiter konfigurieren kannst
100
+ """
101
+ b = __st_minecraft_en.create_bar(name, anzeige_text)
102
+ return BossLeiste.von_englisch(b)
103
+
104
+
105
+ def setze_text(boss_leiste: BossLeiste, anzeige_text: str) -> BossLeiste:
106
+ """Setze den Text der Leiste"""
107
+ b = __st_minecraft_en.set_text(boss_leiste.zu_englisch(), anzeige_text)
108
+ return BossLeiste.von_englisch(b)
109
+
110
+
111
+ def setze_farbe(boss_leiste: BossLeiste, farbe: BossLeisteFarben) -> BossLeiste:
112
+ """Setze die Farbe der Leiste"""
113
+ b = __st_minecraft_en.set_color(boss_leiste.zu_englisch(), farbe.zu_englisch())
114
+ return BossLeiste.von_englisch(b)
115
+
116
+
117
+ def setze_wert(boss_leiste: BossLeiste, wert: float) -> BossLeiste:
118
+ """Setze zu welchem Anteil die leiste gefüllt sein soll (zwischen 0 und 1)"""
119
+ if not 0 <= wert <= 1:
120
+ raise WertFehler(f"Der Wert der Bossleiste muss zwischen 0 und 1 liegen. Du hast '{wert}' angegeben.")
121
+
122
+ b = __st_minecraft_en.set_value(boss_leiste.zu_englisch(), wert)
123
+ return BossLeiste.von_englisch(b)
124
+
125
+
126
+ def setze_stil(boss_leiste: BossLeiste, stil: BossLeisteStil) -> BossLeiste:
127
+ """Setze den Stil der Leiste"""
128
+ b = __st_minecraft_en.set_style(boss_leiste.zu_englisch(), stil.zu_englisch())
129
+ return BossLeiste.von_englisch(b)
130
+
131
+
132
+ def loesche_leiste(boss_leiste: BossLeiste):
133
+ """Lösche eine Leiste"""
134
+ __st_minecraft_en.delete_bar(boss_leiste.zu_englisch())
@@ -0,0 +1,309 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from st_minecraft.core.core import InventoryFieldEmptyError
7
+ from st_minecraft.core.core import _to_enum
8
+ from st_minecraft.de.entity import EntitySammlung
9
+ from st_minecraft.de.material import MaterialSammlung
10
+ from st_minecraft.en import Dimension
11
+ from st_minecraft.en import DirectionCollection as _DirectionCollection
12
+ from st_minecraft.en import Entity as _EntityEN
13
+ from st_minecraft.en import Inventory as _InventoryEN
14
+ from st_minecraft.en import InventoryField as _InventoryFieldEN
15
+ from st_minecraft.en import Item as _ItemEN
16
+ from st_minecraft.en import Material as _MaterialEN
17
+ from st_minecraft.en import Message as _MessageEN
18
+ from st_minecraft.en import Player as _PlayerEN
19
+
20
+
21
+ class RichtungSammlung(Enum):
22
+ """
23
+ Arten, wie Geschwindigkeiten verändert werden können
24
+ Wird u.A. in spieler_geschwindigkeit_setzen() verwendet.
25
+ """
26
+
27
+ Hoch = "UP"
28
+ Runter = "DOWN"
29
+ Zurück = "BACK"
30
+ Vorwärts = "LOOKING"
31
+
32
+ @staticmethod
33
+ def von_englisch(e: _DirectionCollection) -> "RichtungSammlung":
34
+ return RichtungSammlung._value2member_map_[e.value]
35
+
36
+ def zu_englisch(self) -> _DirectionCollection:
37
+ return _DirectionCollection._value2member_map_[self.value]
38
+
39
+
40
+ class Material(BaseModel):
41
+ """
42
+ Modelliert einen Block in Minecraft, der sich zum Zeitpunkt der Abfrage an einer bestimmten Koordinate befindet.
43
+ """
44
+
45
+ typ: MaterialSammlung | None
46
+ """ Block Typ """
47
+ x: int | None = None
48
+ y: int | None = None
49
+ z: int | None = None
50
+ dimension: Dimension | None = None
51
+
52
+ def __repr__(self):
53
+ return f"Block(typ={self.typ}, x={self.x}, y={self.y}, z={self.z}, dimension={self.dimension})"
54
+
55
+ @staticmethod
56
+ def von_englisch(m: _MaterialEN | None) -> Optional["Material"]:
57
+ if m is None:
58
+ return None
59
+
60
+ return Material(
61
+ typ=MaterialSammlung.von_englisch(m.type),
62
+ x=m.x,
63
+ y=m.y,
64
+ z=m.z,
65
+ dimension=m.dimension,
66
+ )
67
+
68
+ def zu_englisch(self) -> _MaterialEN:
69
+ if self.typ is None:
70
+ return None
71
+
72
+ return _MaterialEN(
73
+ type=self.typ.zu_englisch(),
74
+ x=self.x,
75
+ y=self.y,
76
+ z=self.z,
77
+ dimension=self.dimension,
78
+ )
79
+
80
+
81
+ class Spieler(BaseModel):
82
+ """Momentaufnahme zum Zeitpunkt der Abfrage, die Daten werden NICHT dauerhaft aktualisiert!"""
83
+
84
+ id: int
85
+ """ Eindeutige ID des Spielers """
86
+ name: str
87
+ """ Name des Spielers """
88
+ x: int
89
+ y: int
90
+ z: int
91
+ dimension: Dimension
92
+ rotation: int
93
+ """ Rotation des Spielers von -180 bis 180 """
94
+ schaut_auf: Material | None
95
+ """ Der nächste Block auf den Spieler schaut (maximal 100 Blöcke weit entfernt) """
96
+ sneaked: bool
97
+ """ True wenn Player sneaked """
98
+ max_leben: float
99
+ leben: float
100
+ hunger: float
101
+ sättigung: float
102
+ xp_level: float
103
+ xp_fortschritt: float
104
+
105
+ @staticmethod
106
+ def von_englisch(p: _PlayerEN):
107
+ return Spieler(
108
+ id=p.id,
109
+ name=p.name,
110
+ x=p.x,
111
+ y=p.y,
112
+ z=p.z,
113
+ dimension=p.dimension,
114
+ rotation=p.rotation,
115
+ schaut_auf=Material.von_englisch(p.looking_at),
116
+ sneaked=p.sneaked,
117
+ max_leben=p.max_health,
118
+ leben=p.health,
119
+ hunger=p.hunger,
120
+ sättigung=p.saturation,
121
+ xp_level=p.xp_level,
122
+ xp_fortschritt=p.xp_progress,
123
+ )
124
+
125
+ def zu_englisch(self) -> _PlayerEN:
126
+ return _PlayerEN(
127
+ id=self.id,
128
+ name=self.name,
129
+ x=self.x,
130
+ y=self.y,
131
+ z=self.z,
132
+ dimension=self.dimension,
133
+ rotation=self.rotation,
134
+ looking_at=self.schaut_auf.zu_englisch(),
135
+ sneaked=self.sneaked,
136
+ max_health=self.max_leben,
137
+ health=self.leben,
138
+ hunger=self.hunger,
139
+ saturation=self.sättigung,
140
+ xp_level=self.xp_level,
141
+ xp_progress=self.xp_fortschritt,
142
+ )
143
+
144
+ def __repr__(self):
145
+ return (
146
+ f"Spieler("
147
+ f"id={self.id}, "
148
+ f"name={self.name}, "
149
+ f"x={self.x}, "
150
+ f"y={self.y}, "
151
+ f"z={self.z}, "
152
+ f"dimension={self.dimension}, "
153
+ f"rotation={self.rotation}, "
154
+ f"leben={self.leben}, "
155
+ f"max_leben={self.max_leben}, "
156
+ f"hunger={self.hunger}, "
157
+ f"sättigung={self.sättigung}, "
158
+ f"xp_level={self.xp_level}, "
159
+ f"xp_fortschritt={self.xp_fortschritt}, "
160
+ f"sneaked={self.sneaked}, "
161
+ f"schaut_auf={self.schaut_auf}, "
162
+ ")"
163
+ )
164
+
165
+
166
+ class Entity(BaseModel):
167
+ """Modelliert ein Entity. Viele der Informationen können leer (None) sein"""
168
+
169
+ typ: EntitySammlung
170
+ """ Typ des Entity's """
171
+ id: str | None = None
172
+ """ Einzigartige ID für dieses Entity """
173
+ name: str | None = None
174
+ x: float | None = None
175
+ y: float | None = None
176
+ z: float | None = None
177
+ dimension: Dimension | None = None
178
+ leben: float | None = None
179
+ ai: bool | None = None
180
+
181
+ def __repr__(self):
182
+ return (
183
+ f"Entity("
184
+ f"typ={self.typ}, "
185
+ f"name={self.name}, "
186
+ f"x={self.x}, "
187
+ f"y={self.y}, "
188
+ f"z={self.z}, "
189
+ f"dimension={self.dimension}, "
190
+ f"leben={self.leben}, "
191
+ f"ai={self.ai}, "
192
+ f"id={self.id}, "
193
+ ")"
194
+ )
195
+
196
+ @staticmethod
197
+ def von_string(typ: str):
198
+ # TODO: brauchen wir das? falls nein können wir die default-Nones entfernen
199
+ return Entity(typ=_to_enum(EntitySammlung, typ))
200
+
201
+ @staticmethod
202
+ def von_englisch(e: _EntityEN):
203
+ return Entity(
204
+ typ=EntitySammlung.von_englisch(e.type),
205
+ id=e.id,
206
+ name=e.name,
207
+ x=e.x,
208
+ y=e.y,
209
+ z=e.z,
210
+ dimension=e.dimension,
211
+ leben=e.health,
212
+ ai=e.ai,
213
+ )
214
+
215
+ def zu_englisch(self) -> _EntityEN:
216
+ return _EntityEN(
217
+ type=self.typ.zu_englisch(),
218
+ id=self.id,
219
+ name=self.name,
220
+ x=self.x,
221
+ y=self.y,
222
+ z=self.z,
223
+ dimension=self.dimension,
224
+ health=self.leben,
225
+ ai=self.ai,
226
+ )
227
+
228
+
229
+ class Item(BaseModel):
230
+ """Modelliert ein Item"""
231
+
232
+ typ: MaterialSammlung
233
+ anzeige_name: str | None
234
+
235
+ def __repr__(self):
236
+ return f"Item(typ={self.typ}, anzeige_name={self.anzeige_name})"
237
+
238
+ @staticmethod
239
+ def von_englisch(i: _ItemEN):
240
+ return Item(typ=MaterialSammlung.von_englisch(i.type), anzeige_name=i.display_name)
241
+
242
+
243
+ class InventarFeld(BaseModel):
244
+ """Ein Feld im Inventar eine:r Spieler:in"""
245
+
246
+ index: int
247
+ """ Index wo das Feld im Inventar liegt """
248
+ item: Item
249
+ """ Item Objekt, welches Item in dem Feld liegt """
250
+ anzahl: int
251
+ """ Wie viele von dem Item in diesem Feld liegen """
252
+
253
+ def __repr__(self):
254
+ return f"InventarFeld(index={self.index}, item={self.item!r}, anzahl={self.anzahl})"
255
+
256
+ @staticmethod
257
+ def von_englisch(i: _InventoryFieldEN):
258
+ return InventarFeld(index=i.index, item=Item.von_englisch(i.item), anzahl=i.amount)
259
+
260
+ def zu_englisch(self) -> _InventoryFieldEN:
261
+ return _InventoryFieldEN(index=self.index, item=self.item.zu_englisch(), amount=self.anzahl)
262
+
263
+
264
+ class Inventar(dict[int, InventarFeld]):
265
+ """
266
+ Enthält das gesamte Inventar eines Spielers.
267
+ Die Struktur ist ein dict.
268
+ Das dict zeigt von Index des Inventars auf ein Objekt vom Typ InventarFeld, welcher die Infos über das Element in dem Feld enthält.
269
+ Hinweis: Felder, die Leer sind, sind nicht in dem dict enthalten!
270
+ """
271
+
272
+ def __contains__(self, item: Item):
273
+ """Überprüfe, ob ein Item im Inventar ist"""
274
+ for _, v in self.items():
275
+ if v.item == item:
276
+ return True
277
+ return False
278
+
279
+ def __getitem__(self, item: int):
280
+ try:
281
+ super().__getitem__(item)
282
+ # ich glaube, hier ist der peak der library. ein nicht-generischer wrapper um den KeyError.
283
+ except KeyError:
284
+ raise InventoryFieldEmptyError(f"Das Feld {item} ist leer. Daher kannst du hier nicht drauf zugreifen.")
285
+
286
+ @staticmethod
287
+ def von_englisch(i: _InventoryEN):
288
+ return Inventar({index: InventarFeld.von_englisch(field) for index, field in i.items()})
289
+
290
+ def zu_englisch(self) -> _InventoryEN:
291
+ return _InventoryEN({index: field.zu_englisch() for index, field in self.items()})
292
+
293
+
294
+ class Nachricht(BaseModel):
295
+ """
296
+ Repräsentiert eine Nachricht, die vom Nutzer an den Chat gesendet wurde
297
+ """
298
+
299
+ text: str
300
+ """Der in den Chat gesendete Text"""
301
+ sender_name: str
302
+ """Name des Spielers, der diese Nachricht gesendet hat"""
303
+
304
+ @staticmethod
305
+ def von_englisch(m: _MessageEN):
306
+ return Nachricht(text=m.text, absender_name=m.sender_name)
307
+
308
+ def zu_englisch(self) -> _MessageEN:
309
+ return _MessageEN(text=self.text, sender_name=self.absender_name)