empire-core 0.7.3__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 (67) hide show
  1. empire_core/__init__.py +36 -0
  2. empire_core/_archive/actions.py +511 -0
  3. empire_core/_archive/automation/__init__.py +24 -0
  4. empire_core/_archive/automation/alliance_tools.py +266 -0
  5. empire_core/_archive/automation/battle_reports.py +196 -0
  6. empire_core/_archive/automation/building_queue.py +242 -0
  7. empire_core/_archive/automation/defense_manager.py +124 -0
  8. empire_core/_archive/automation/map_scanner.py +370 -0
  9. empire_core/_archive/automation/multi_account.py +296 -0
  10. empire_core/_archive/automation/quest_automation.py +94 -0
  11. empire_core/_archive/automation/resource_manager.py +380 -0
  12. empire_core/_archive/automation/target_finder.py +153 -0
  13. empire_core/_archive/automation/tasks.py +224 -0
  14. empire_core/_archive/automation/unit_production.py +719 -0
  15. empire_core/_archive/cli.py +68 -0
  16. empire_core/_archive/client_async.py +469 -0
  17. empire_core/_archive/commands.py +201 -0
  18. empire_core/_archive/connection_async.py +228 -0
  19. empire_core/_archive/defense.py +156 -0
  20. empire_core/_archive/events/__init__.py +35 -0
  21. empire_core/_archive/events/base.py +153 -0
  22. empire_core/_archive/events/manager.py +85 -0
  23. empire_core/accounts.py +190 -0
  24. empire_core/client/__init__.py +0 -0
  25. empire_core/client/client.py +459 -0
  26. empire_core/config.py +87 -0
  27. empire_core/exceptions.py +42 -0
  28. empire_core/network/__init__.py +0 -0
  29. empire_core/network/connection.py +378 -0
  30. empire_core/protocol/__init__.py +0 -0
  31. empire_core/protocol/models/__init__.py +339 -0
  32. empire_core/protocol/models/alliance.py +186 -0
  33. empire_core/protocol/models/army.py +444 -0
  34. empire_core/protocol/models/attack.py +229 -0
  35. empire_core/protocol/models/auth.py +216 -0
  36. empire_core/protocol/models/base.py +403 -0
  37. empire_core/protocol/models/building.py +455 -0
  38. empire_core/protocol/models/castle.py +317 -0
  39. empire_core/protocol/models/chat.py +150 -0
  40. empire_core/protocol/models/defense.py +300 -0
  41. empire_core/protocol/models/map.py +269 -0
  42. empire_core/protocol/packet.py +104 -0
  43. empire_core/services/__init__.py +31 -0
  44. empire_core/services/alliance.py +222 -0
  45. empire_core/services/base.py +107 -0
  46. empire_core/services/castle.py +221 -0
  47. empire_core/state/__init__.py +0 -0
  48. empire_core/state/manager.py +398 -0
  49. empire_core/state/models.py +215 -0
  50. empire_core/state/quest_models.py +60 -0
  51. empire_core/state/report_models.py +115 -0
  52. empire_core/state/unit_models.py +75 -0
  53. empire_core/state/world_models.py +269 -0
  54. empire_core/storage/__init__.py +1 -0
  55. empire_core/storage/database.py +237 -0
  56. empire_core/utils/__init__.py +0 -0
  57. empire_core/utils/battle_sim.py +172 -0
  58. empire_core/utils/calculations.py +170 -0
  59. empire_core/utils/crypto.py +8 -0
  60. empire_core/utils/decorators.py +69 -0
  61. empire_core/utils/enums.py +111 -0
  62. empire_core/utils/helpers.py +252 -0
  63. empire_core/utils/response_awaiter.py +153 -0
  64. empire_core/utils/troops.py +93 -0
  65. empire_core-0.7.3.dist-info/METADATA +197 -0
  66. empire_core-0.7.3.dist-info/RECORD +67 -0
  67. empire_core-0.7.3.dist-info/WHEEL +4 -0
@@ -0,0 +1,378 @@
1
+ """
2
+ Synchronous WebSocket connection for EmpireCore.
3
+
4
+ Uses websocket-client library with a dedicated receive thread.
5
+ Designed to work well with Discord.py by not competing for the event loop.
6
+ """
7
+
8
+ import logging
9
+ import threading
10
+ import time
11
+ from dataclasses import dataclass, field
12
+ from typing import Callable, Dict, List, Optional
13
+
14
+ import websocket
15
+
16
+ from empire_core.protocol.packet import Packet
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @dataclass
22
+ class ResponseWaiter:
23
+ """A waiter for a specific command response."""
24
+
25
+ event: threading.Event = field(default_factory=threading.Event)
26
+ result: Optional[Packet] = None
27
+ error: Optional[Exception] = None
28
+
29
+
30
+ class Connection:
31
+ """
32
+ Synchronous WebSocket connection with threaded message routing.
33
+
34
+ Features:
35
+ - Request/response pattern via waiters (consumed on match)
36
+ - Pub/sub pattern via subscribers (broadcast to all)
37
+ - Automatic keepalive thread
38
+ - Thread-safe operations
39
+ """
40
+
41
+ def __init__(self, url: str):
42
+ self.url = url
43
+ self.ws: Optional[websocket.WebSocket] = None
44
+
45
+ self._running = False
46
+ self._recv_thread: Optional[threading.Thread] = None
47
+ self._keepalive_thread: Optional[threading.Thread] = None
48
+
49
+ # Request/response waiters: cmd_id -> ResponseWaiter
50
+ # These are consumed when matched (one response per waiter)
51
+ self._waiters: Dict[str, List[ResponseWaiter]] = {}
52
+ self._waiters_lock = threading.Lock()
53
+
54
+ # Pub/sub subscribers: cmd_id -> list of callbacks
55
+ # These receive copies of all matching packets
56
+ self._subscribers: Dict[str, List[Callable[[Packet], None]]] = {}
57
+ self._subscribers_lock = threading.Lock()
58
+
59
+ # Global packet handler (for state updates, etc.)
60
+ self.on_packet: Optional[Callable[[Packet], None]] = None
61
+
62
+ # Disconnect callback
63
+ self.on_disconnect: Optional[Callable[[], None]] = None
64
+
65
+ @property
66
+ def connected(self) -> bool:
67
+ """Check if connection is active."""
68
+ return self.ws is not None and self.ws.connected and self._running
69
+
70
+ def connect(self, timeout: float = 10.0) -> None:
71
+ """
72
+ Connect to the WebSocket server.
73
+
74
+ Args:
75
+ timeout: Connection timeout in seconds
76
+ """
77
+ if self.connected:
78
+ logger.warning("Already connected")
79
+ return
80
+
81
+ logger.info(f"Connecting to {self.url}...")
82
+
83
+ self.ws = websocket.WebSocket()
84
+ self.ws.settimeout(timeout)
85
+
86
+ try:
87
+ self.ws.connect(self.url)
88
+ self._running = True
89
+
90
+ # Start receive thread
91
+ self._recv_thread = threading.Thread(
92
+ target=self._recv_loop,
93
+ name="EmpireCore-Recv",
94
+ daemon=True,
95
+ )
96
+ self._recv_thread.start()
97
+
98
+ # Start keepalive thread
99
+ self._keepalive_thread = threading.Thread(
100
+ target=self._keepalive_loop,
101
+ name="EmpireCore-Keepalive",
102
+ daemon=True,
103
+ )
104
+ self._keepalive_thread.start()
105
+
106
+ logger.info("Connected successfully")
107
+
108
+ except Exception as e:
109
+ logger.error(f"Connection failed: {e}")
110
+ self._cleanup()
111
+ raise
112
+
113
+ def disconnect(self) -> None:
114
+ """Disconnect from the server and cleanup resources."""
115
+ if not self._running:
116
+ return
117
+
118
+ logger.info("Disconnecting...")
119
+ self._running = False
120
+
121
+ # Cancel all waiters
122
+ self._cancel_all_waiters()
123
+
124
+ # Close websocket
125
+ self._cleanup()
126
+
127
+ # Wait for threads to finish
128
+ if self._recv_thread and self._recv_thread.is_alive():
129
+ self._recv_thread.join(timeout=2.0)
130
+ if self._keepalive_thread and self._keepalive_thread.is_alive():
131
+ self._keepalive_thread.join(timeout=2.0)
132
+
133
+ logger.info("Disconnected")
134
+
135
+ def _cleanup(self) -> None:
136
+ """Close websocket connection."""
137
+ if self.ws:
138
+ try:
139
+ self.ws.close()
140
+ except Exception:
141
+ pass
142
+ self.ws = None
143
+
144
+ def send(self, data: str) -> None:
145
+ """
146
+ Send data to the server.
147
+
148
+ Args:
149
+ data: String data to send
150
+
151
+ Raises:
152
+ RuntimeError: If not connected
153
+ """
154
+ if not self.connected:
155
+ raise RuntimeError("Not connected")
156
+
157
+ # Remove null terminator if present (we'll add it)
158
+ if data.endswith("\x00"):
159
+ data = data[:-1]
160
+
161
+ try:
162
+ if self.ws is None:
163
+ raise RuntimeError("Not connected")
164
+ self.ws.send(data)
165
+ logger.debug(f"Sent: {data[:100]}...")
166
+ except Exception as e:
167
+ logger.error(f"Send failed: {e}")
168
+ raise
169
+
170
+ def send_bytes(self, data: bytes) -> None:
171
+ """Send raw bytes to the server."""
172
+ self.send(data.decode("utf-8"))
173
+
174
+ def wait_for(
175
+ self,
176
+ cmd_id: str,
177
+ timeout: float = 5.0,
178
+ ) -> Packet:
179
+ """
180
+ Wait for a response with the given command ID.
181
+
182
+ This is a blocking call that waits for a matching packet.
183
+ The waiter is consumed when a match is found.
184
+
185
+ Args:
186
+ cmd_id: Command ID to wait for
187
+ timeout: Timeout in seconds
188
+
189
+ Returns:
190
+ The matching Packet
191
+
192
+ Raises:
193
+ TimeoutError: If no response within timeout
194
+ RuntimeError: If connection closed while waiting
195
+ """
196
+ waiter = ResponseWaiter()
197
+
198
+ with self._waiters_lock:
199
+ if cmd_id not in self._waiters:
200
+ self._waiters[cmd_id] = []
201
+ self._waiters[cmd_id].append(waiter)
202
+
203
+ try:
204
+ if waiter.event.wait(timeout=timeout):
205
+ if waiter.error:
206
+ raise waiter.error
207
+ if waiter.result:
208
+ return waiter.result
209
+ raise RuntimeError("Waiter completed without result")
210
+ else:
211
+ raise TimeoutError(f"Timeout waiting for '{cmd_id}'")
212
+ finally:
213
+ # Clean up waiter
214
+ with self._waiters_lock:
215
+ if cmd_id in self._waiters:
216
+ try:
217
+ self._waiters[cmd_id].remove(waiter)
218
+ if not self._waiters[cmd_id]:
219
+ del self._waiters[cmd_id]
220
+ except ValueError:
221
+ pass
222
+
223
+ def subscribe(self, cmd_id: str, callback: Callable[[Packet], None]) -> None:
224
+ """
225
+ Subscribe to packets with the given command ID.
226
+
227
+ Unlike waiters, subscribers receive ALL matching packets
228
+ and are not consumed.
229
+
230
+ Args:
231
+ cmd_id: Command ID to subscribe to
232
+ callback: Function to call with matching packets
233
+ """
234
+ with self._subscribers_lock:
235
+ if cmd_id not in self._subscribers:
236
+ self._subscribers[cmd_id] = []
237
+ self._subscribers[cmd_id].append(callback)
238
+
239
+ def unsubscribe(self, cmd_id: str, callback: Callable[[Packet], None]) -> None:
240
+ """Remove a subscriber."""
241
+ with self._subscribers_lock:
242
+ if cmd_id in self._subscribers:
243
+ try:
244
+ self._subscribers[cmd_id].remove(callback)
245
+ if not self._subscribers[cmd_id]:
246
+ del self._subscribers[cmd_id]
247
+ except ValueError:
248
+ pass
249
+
250
+ def _recv_loop(self) -> None:
251
+ """Background thread that receives and routes messages."""
252
+ logger.debug("Receive loop started")
253
+
254
+ while self._running:
255
+ try:
256
+ if not self.ws:
257
+ break
258
+
259
+ # Set a timeout so we can check _running periodically
260
+ self.ws.settimeout(1.0)
261
+
262
+ try:
263
+ data = self.ws.recv()
264
+ except websocket.WebSocketTimeoutException:
265
+ continue # Check _running and try again
266
+
267
+ if not data:
268
+ continue
269
+
270
+ # Parse packet
271
+ if isinstance(data, bytes):
272
+ packet = Packet.from_bytes(data)
273
+ else:
274
+ packet = Packet.from_bytes(data.encode("utf-8"))
275
+
276
+ # Route the packet
277
+ self._route_packet(packet)
278
+
279
+ except websocket.WebSocketConnectionClosedException:
280
+ logger.warning("Connection closed by server")
281
+ break
282
+ except Exception as e:
283
+ if self._running:
284
+ logger.error(f"Error in receive loop: {e}")
285
+ break
286
+
287
+ # Connection lost
288
+ self._running = False
289
+ self._cancel_all_waiters()
290
+
291
+ if self.on_disconnect:
292
+ try:
293
+ self.on_disconnect()
294
+ except Exception as e:
295
+ logger.error(f"Error in disconnect callback: {e}")
296
+
297
+ logger.debug("Receive loop ended")
298
+
299
+ def _route_packet(self, packet: Packet) -> None:
300
+ """
301
+ Route a packet to waiters and subscribers.
302
+
303
+ Order:
304
+ 1. Check waiters (consumed on match)
305
+ 2. Notify subscribers (broadcast)
306
+ 3. Call global handler
307
+
308
+ Uses copy-on-read pattern to minimize lock hold time.
309
+ """
310
+ cmd_id = packet.command_id
311
+
312
+ waiter = None
313
+ callbacks = None
314
+
315
+ # Acquire locks briefly just to extract what we need
316
+ if cmd_id:
317
+ # Check waiters (request/response pattern)
318
+ with self._waiters_lock:
319
+ waiters_list = self._waiters.get(cmd_id)
320
+ if waiters_list:
321
+ waiter = waiters_list.pop(0)
322
+ if not waiters_list:
323
+ del self._waiters[cmd_id]
324
+
325
+ # Get subscriber callbacks (copy the list)
326
+ with self._subscribers_lock:
327
+ subs = self._subscribers.get(cmd_id)
328
+ if subs:
329
+ callbacks = list(subs)
330
+
331
+ # Now dispatch outside of locks
332
+ if waiter:
333
+ waiter.result = packet
334
+ waiter.event.set()
335
+
336
+ if callbacks:
337
+ for callback in callbacks:
338
+ try:
339
+ callback(packet)
340
+ except Exception as e:
341
+ logger.error(f"Subscriber error: {e}")
342
+
343
+ # Global handler
344
+ if self.on_packet:
345
+ try:
346
+ self.on_packet(packet)
347
+ except Exception as e:
348
+ logger.error(f"Packet handler error: {e}")
349
+
350
+ def _keepalive_loop(self) -> None:
351
+ """Background thread that sends keepalive pings."""
352
+ logger.debug("Keepalive loop started")
353
+
354
+ while self._running:
355
+ time.sleep(30)
356
+
357
+ if not self._running:
358
+ break
359
+
360
+ try:
361
+ # Send GGE-specific keepalive
362
+ self.send("%xt%EmpireEx_21%pin%1%<RoundHouseKick>%")
363
+ logger.debug("Sent keepalive ping")
364
+ except Exception as e:
365
+ if self._running:
366
+ logger.error(f"Keepalive failed: {e}")
367
+ break
368
+
369
+ logger.debug("Keepalive loop ended")
370
+
371
+ def _cancel_all_waiters(self) -> None:
372
+ """Cancel all pending waiters."""
373
+ with self._waiters_lock:
374
+ for waiters in self._waiters.values():
375
+ for waiter in waiters:
376
+ waiter.error = RuntimeError("Connection closed")
377
+ waiter.event.set()
378
+ self._waiters.clear()
File without changes
@@ -0,0 +1,339 @@
1
+ """
2
+ GGE Protocol Models - Pydantic models for Goodgame Empire protocol commands.
3
+
4
+ This package contains type-safe models for all GGE protocol commands extracted
5
+ from pygge and GGE-BOT implementations.
6
+
7
+ Structure:
8
+ - base.py: Base classes and common types
9
+ - auth.py: Authentication commands (lli, lre, vpn, vln, etc.)
10
+ - chat.py: Alliance chat commands (acm, acl)
11
+ - alliance.py: Alliance commands (ahc, aha, ahr)
12
+ - castle.py: Castle commands (gcl, dcl, jca, arc, etc.)
13
+ - map.py: Map commands (gaa, gam, fnm, adi, etc.)
14
+ - attack.py: Attack/spy commands (cra, csm, gas, etc.)
15
+ - building.py: Building commands (ebu, eup, emo, sbd, etc.)
16
+ - army.py: Army/soldier/hospital commands (bup, spl, gui, hru, etc.)
17
+ - defense.py: Defense commands (dfc, dfk, dfw, dfm)
18
+
19
+ TODO (lower priority):
20
+ - shop.py: Shop/purchase commands (sbp, gbc)
21
+ - events.py: Event commands (sei, pep, hgh, sede)
22
+ - gifts.py: Gift commands (clb, gpg)
23
+ - quests.py: Quest commands (qsc, qdr, fcq)
24
+ - account.py: Account commands (gpi, scp, vpm, cpne, etc.)
25
+ - settings.py: Settings commands (ani, mvf, opt, hfl)
26
+ - misc.py: Miscellaneous commands (txi, txs, txc, gbl, etc.)
27
+ """
28
+
29
+ from .alliance import (
30
+ AskHelpRequest,
31
+ AskHelpResponse,
32
+ HelpAllRequest,
33
+ HelpAllResponse,
34
+ HelpMemberRequest,
35
+ HelpMemberResponse,
36
+ HelpRequestNotification,
37
+ )
38
+ from .army import (
39
+ CancelHealRequest,
40
+ CancelHealResponse,
41
+ CancelProductionRequest,
42
+ CancelProductionResponse,
43
+ DeleteUnitsRequest,
44
+ DeleteUnitsResponse,
45
+ DeleteWoundedRequest,
46
+ DeleteWoundedResponse,
47
+ DoubleProductionRequest,
48
+ DoubleProductionResponse,
49
+ GetProductionQueueRequest,
50
+ GetProductionQueueResponse,
51
+ GetUnitsRequest,
52
+ GetUnitsResponse,
53
+ HealAllRequest,
54
+ HealAllResponse,
55
+ HealUnitsRequest,
56
+ HealUnitsResponse,
57
+ ProduceUnitsRequest,
58
+ ProduceUnitsResponse,
59
+ ProductionQueueItem,
60
+ SkipHealRequest,
61
+ SkipHealResponse,
62
+ )
63
+ from .attack import (
64
+ AttackPreset,
65
+ CreateAttackRequest,
66
+ CreateAttackResponse,
67
+ GetPresetsRequest,
68
+ GetPresetsResponse,
69
+ SendSpyRequest,
70
+ SendSpyResponse,
71
+ SkipAttackCooldownRequest,
72
+ SkipAttackCooldownResponse,
73
+ SkipDefenseCooldownRequest,
74
+ SkipDefenseCooldownResponse,
75
+ )
76
+ from .auth import (
77
+ CheckUsernameAvailableRequest,
78
+ CheckUsernameAvailableResponse,
79
+ CheckUsernameExistsRequest,
80
+ CheckUsernameExistsResponse,
81
+ LoginRequest,
82
+ LoginResponse,
83
+ PasswordRecoveryRequest,
84
+ PasswordRecoveryResponse,
85
+ PlayerData,
86
+ RegisterRequest,
87
+ RegisterResponse,
88
+ )
89
+ from .base import (
90
+ DEFAULT_ZONE,
91
+ BasePayload,
92
+ BaseRequest,
93
+ BaseResponse,
94
+ ErrorResponse,
95
+ GGECommand,
96
+ HelpType,
97
+ PlayerInfo,
98
+ Position,
99
+ ProductionListType,
100
+ ResourceAmount,
101
+ UnitCount,
102
+ decode_chat_text,
103
+ encode_chat_text,
104
+ get_response_model,
105
+ parse_response,
106
+ )
107
+ from .building import (
108
+ BuildRequest,
109
+ BuildResponse,
110
+ BuyExtensionRequest,
111
+ BuyExtensionResponse,
112
+ CollectExtensionGiftRequest,
113
+ CollectExtensionGiftResponse,
114
+ DestroyBuildingRequest,
115
+ DestroyBuildingResponse,
116
+ FastCompleteRequest,
117
+ FastCompleteResponse,
118
+ MoveBuildingRequest,
119
+ MoveBuildingResponse,
120
+ RepairAllRequest,
121
+ RepairAllResponse,
122
+ RepairBuildingRequest,
123
+ RepairBuildingResponse,
124
+ SellBuildingRequest,
125
+ SellBuildingResponse,
126
+ TimeSkipBuildingRequest,
127
+ TimeSkipBuildingResponse,
128
+ UpgradeBuildingRequest,
129
+ UpgradeBuildingResponse,
130
+ UpgradeWallRequest,
131
+ UpgradeWallResponse,
132
+ )
133
+ from .castle import (
134
+ BuildingInfo,
135
+ CastleInfo,
136
+ DetailedCastleInfo,
137
+ GetCastlesRequest,
138
+ GetCastlesResponse,
139
+ GetDetailedCastleRequest,
140
+ GetDetailedCastleResponse,
141
+ GetProductionRequest,
142
+ GetProductionResponse,
143
+ GetResourcesRequest,
144
+ GetResourcesResponse,
145
+ ProductionRates,
146
+ RelocateCastleRequest,
147
+ RelocateCastleResponse,
148
+ RenameCastleRequest,
149
+ RenameCastleResponse,
150
+ SelectCastleRequest,
151
+ SelectCastleResponse,
152
+ )
153
+ from .chat import (
154
+ AllianceChatLogRequest,
155
+ AllianceChatLogResponse,
156
+ AllianceChatMessageRequest,
157
+ AllianceChatMessageResponse,
158
+ ChatLogEntry,
159
+ ChatMessageData,
160
+ )
161
+ from .defense import (
162
+ ChangeKeepDefenseRequest,
163
+ ChangeKeepDefenseResponse,
164
+ ChangeMoatDefenseRequest,
165
+ ChangeMoatDefenseResponse,
166
+ ChangeWallDefenseRequest,
167
+ ChangeWallDefenseResponse,
168
+ DefenseConfiguration,
169
+ GetDefenseRequest,
170
+ GetDefenseResponse,
171
+ GetSupportDefenseRequest,
172
+ GetSupportDefenseResponse,
173
+ )
174
+ from .map import (
175
+ FindNPCRequest,
176
+ FindNPCResponse,
177
+ GetMapAreaRequest,
178
+ GetMapAreaResponse,
179
+ GetMovementsRequest,
180
+ GetMovementsResponse,
181
+ GetTargetInfoRequest,
182
+ GetTargetInfoResponse,
183
+ MapObject,
184
+ Movement,
185
+ NPCLocation,
186
+ TargetInfo,
187
+ )
188
+
189
+ __all__ = [
190
+ # Base
191
+ "GGECommand",
192
+ "DEFAULT_ZONE",
193
+ "HelpType",
194
+ "ProductionListType",
195
+ "BasePayload",
196
+ "BaseRequest",
197
+ "BaseResponse",
198
+ "ErrorResponse",
199
+ "Position",
200
+ "ResourceAmount",
201
+ "UnitCount",
202
+ "PlayerInfo",
203
+ "encode_chat_text",
204
+ "decode_chat_text",
205
+ "get_response_model",
206
+ "parse_response",
207
+ # Auth
208
+ "LoginRequest",
209
+ "LoginResponse",
210
+ "PlayerData",
211
+ "RegisterRequest",
212
+ "RegisterResponse",
213
+ "CheckUsernameAvailableRequest",
214
+ "CheckUsernameAvailableResponse",
215
+ "CheckUsernameExistsRequest",
216
+ "CheckUsernameExistsResponse",
217
+ "PasswordRecoveryRequest",
218
+ "PasswordRecoveryResponse",
219
+ # Chat
220
+ "AllianceChatMessageRequest",
221
+ "AllianceChatMessageResponse",
222
+ "ChatMessageData",
223
+ "AllianceChatLogRequest",
224
+ "AllianceChatLogResponse",
225
+ "ChatLogEntry",
226
+ # Alliance
227
+ "HelpMemberRequest",
228
+ "HelpMemberResponse",
229
+ "HelpAllRequest",
230
+ "HelpAllResponse",
231
+ "AskHelpRequest",
232
+ "AskHelpResponse",
233
+ "HelpRequestNotification",
234
+ # Castle
235
+ "GetCastlesRequest",
236
+ "GetCastlesResponse",
237
+ "CastleInfo",
238
+ "GetDetailedCastleRequest",
239
+ "GetDetailedCastleResponse",
240
+ "DetailedCastleInfo",
241
+ "BuildingInfo",
242
+ "SelectCastleRequest",
243
+ "SelectCastleResponse",
244
+ "RenameCastleRequest",
245
+ "RenameCastleResponse",
246
+ "RelocateCastleRequest",
247
+ "RelocateCastleResponse",
248
+ "GetResourcesRequest",
249
+ "GetResourcesResponse",
250
+ "GetProductionRequest",
251
+ "GetProductionResponse",
252
+ "ProductionRates",
253
+ # Map
254
+ "GetMapAreaRequest",
255
+ "GetMapAreaResponse",
256
+ "MapObject",
257
+ "GetMovementsRequest",
258
+ "GetMovementsResponse",
259
+ "Movement",
260
+ "FindNPCRequest",
261
+ "FindNPCResponse",
262
+ "NPCLocation",
263
+ "GetTargetInfoRequest",
264
+ "GetTargetInfoResponse",
265
+ "TargetInfo",
266
+ # Attack
267
+ "CreateAttackRequest",
268
+ "CreateAttackResponse",
269
+ "SendSpyRequest",
270
+ "SendSpyResponse",
271
+ "GetPresetsRequest",
272
+ "GetPresetsResponse",
273
+ "AttackPreset",
274
+ "SkipAttackCooldownRequest",
275
+ "SkipAttackCooldownResponse",
276
+ "SkipDefenseCooldownRequest",
277
+ "SkipDefenseCooldownResponse",
278
+ # Building
279
+ "BuildRequest",
280
+ "BuildResponse",
281
+ "UpgradeBuildingRequest",
282
+ "UpgradeBuildingResponse",
283
+ "MoveBuildingRequest",
284
+ "MoveBuildingResponse",
285
+ "SellBuildingRequest",
286
+ "SellBuildingResponse",
287
+ "DestroyBuildingRequest",
288
+ "DestroyBuildingResponse",
289
+ "FastCompleteRequest",
290
+ "FastCompleteResponse",
291
+ "TimeSkipBuildingRequest",
292
+ "TimeSkipBuildingResponse",
293
+ "UpgradeWallRequest",
294
+ "UpgradeWallResponse",
295
+ "RepairBuildingRequest",
296
+ "RepairBuildingResponse",
297
+ "RepairAllRequest",
298
+ "RepairAllResponse",
299
+ "BuyExtensionRequest",
300
+ "BuyExtensionResponse",
301
+ "CollectExtensionGiftRequest",
302
+ "CollectExtensionGiftResponse",
303
+ # Army
304
+ "ProduceUnitsRequest",
305
+ "ProduceUnitsResponse",
306
+ "GetProductionQueueRequest",
307
+ "GetProductionQueueResponse",
308
+ "ProductionQueueItem",
309
+ "DoubleProductionRequest",
310
+ "DoubleProductionResponse",
311
+ "CancelProductionRequest",
312
+ "CancelProductionResponse",
313
+ "GetUnitsRequest",
314
+ "GetUnitsResponse",
315
+ "DeleteUnitsRequest",
316
+ "DeleteUnitsResponse",
317
+ "HealUnitsRequest",
318
+ "HealUnitsResponse",
319
+ "CancelHealRequest",
320
+ "CancelHealResponse",
321
+ "SkipHealRequest",
322
+ "SkipHealResponse",
323
+ "DeleteWoundedRequest",
324
+ "DeleteWoundedResponse",
325
+ "HealAllRequest",
326
+ "HealAllResponse",
327
+ # Defense
328
+ "GetDefenseRequest",
329
+ "GetDefenseResponse",
330
+ "DefenseConfiguration",
331
+ "ChangeKeepDefenseRequest",
332
+ "ChangeKeepDefenseResponse",
333
+ "ChangeWallDefenseRequest",
334
+ "ChangeWallDefenseResponse",
335
+ "ChangeMoatDefenseRequest",
336
+ "ChangeMoatDefenseResponse",
337
+ "GetSupportDefenseRequest",
338
+ "GetSupportDefenseResponse",
339
+ ]