albibong 1.0.6__tar.gz → 1.1.0__tar.gz

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 (80) hide show
  1. {albibong-1.0.6 → albibong-1.1.0}/.gitignore +2 -0
  2. {albibong-1.0.6 → albibong-1.1.0}/PKG-INFO +2 -1
  3. {albibong-1.0.6 → albibong-1.1.0}/pyproject.toml +4 -2
  4. albibong-1.1.0/src/albibong/__init__.py +147 -0
  5. albibong-1.1.0/src/albibong/classes/dungeon.py +66 -0
  6. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/__init__.py +10 -0
  7. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_character_equipment_changed.py +2 -10
  8. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_party.py +6 -13
  9. albibong-1.1.0/src/albibong/classes/event_handler/handle_operation_change_cluster.py +38 -0
  10. albibong-1.1.0/src/albibong/classes/event_handler/handle_operation_farmable_harvest.py +17 -0
  11. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_operation_join.py +12 -29
  12. albibong-1.1.0/src/albibong/classes/event_handler/world_data_utils.py +155 -0
  13. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/item.py +19 -4
  14. albibong-1.1.0/src/albibong/classes/location.py +151 -0
  15. albibong-1.1.0/src/albibong/gui_dist/assets/index-C4r00CBk.js +168 -0
  16. albibong-1.1.0/src/albibong/gui_dist/assets/index-DKbORdbN.css +1 -0
  17. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/index.html +2 -2
  18. albibong-1.1.0/src/albibong/models/models.py +12 -0
  19. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/requirements.txt +1 -0
  20. albibong-1.1.0/src/albibong/resources/EventCode.py +593 -0
  21. albibong-1.1.0/src/albibong/resources/OperationCode.py +502 -0
  22. albibong-1.1.0/src/albibong/resources/event_code.json +591 -0
  23. albibong-1.0.6/src/albibong/resources/items.json → albibong-1.1.0/src/albibong/resources/items_by_id.json +21767 -21108
  24. albibong-1.1.0/src/albibong/resources/items_by_unique_name.json +55282 -0
  25. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/resources/maps.json +755 -514
  26. albibong-1.1.0/src/albibong/resources/operation_code.json +500 -0
  27. albibong-1.1.0/src/albibong/threads/__init__.py +0 -0
  28. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/threads/websocket_server.py +64 -41
  29. albibong-1.0.6/src/albibong/__init__.py +0 -89
  30. albibong-1.0.6/src/albibong/classes/dungeon.py +0 -59
  31. albibong-1.0.6/src/albibong/classes/event_handler/handle_operation_change_cluster.py +0 -43
  32. albibong-1.0.6/src/albibong/classes/event_handler/world_data_utils.py +0 -102
  33. albibong-1.0.6/src/albibong/classes/location.py +0 -48
  34. albibong-1.0.6/src/albibong/gui_dist/assets/index-DZvgNqlG.css +0 -1
  35. albibong-1.0.6/src/albibong/gui_dist/assets/index-Dt6hyZiS.css +0 -1
  36. albibong-1.0.6/src/albibong/gui_dist/assets/index-E7pha23k.js +0 -161
  37. albibong-1.0.6/src/albibong/gui_dist/assets/index-WIuC9Mnh.js +0 -161
  38. albibong-1.0.6/src/albibong/resources/EventCode.py +0 -588
  39. albibong-1.0.6/src/albibong/resources/OperationCode.py +0 -499
  40. albibong-1.0.6/src/albibong/resources/event_code.json +0 -586
  41. albibong-1.0.6/src/albibong/resources/operation_code.json +0 -497
  42. {albibong-1.0.6 → albibong-1.1.0}/LICENSE +0 -0
  43. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/__main__.py +0 -0
  44. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/assets/boom_small.mp3 +0 -0
  45. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/__init__.py +0 -0
  46. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/character.py +0 -0
  47. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/coords.py +0 -0
  48. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_health.py +0 -0
  49. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_in_combat_state_update.py +0 -0
  50. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_new_character.py +0 -0
  51. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_other_grabbed_loot.py +0 -0
  52. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_update_fame.py +0 -0
  53. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_event_update_re_spec_points.py +0 -0
  54. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/event_handler/handle_operation_move.py +0 -0
  55. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/logger.py +0 -0
  56. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/packet_handler.py +0 -0
  57. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/utils.py +0 -0
  58. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/classes/world_data.py +0 -0
  59. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/Albibong.png +0 -0
  60. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/No Equipment.png +0 -0
  61. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/fame.png +0 -0
  62. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/re_spec.png +0 -0
  63. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/silver.png +0 -0
  64. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/gui_dist/vite.svg +0 -0
  65. {albibong-1.0.6/src/albibong/threads → albibong-1.1.0/src/albibong/models}/__init__.py +0 -0
  66. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/__init__.py +0 -0
  67. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/command_type.py +0 -0
  68. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/crc_calculator.py +0 -0
  69. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/event_data.py +0 -0
  70. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/message_type.py +0 -0
  71. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/number_serializer.py +0 -0
  72. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/operation_request.py +0 -0
  73. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/operation_response.py +0 -0
  74. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/photon_packet_parser.py +0 -0
  75. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/protocol16_deserializer.py +0 -0
  76. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/protocol16_type.py +0 -0
  77. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/photon_packet_parser/segmented_packet.py +0 -0
  78. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/threads/http_server.py +0 -0
  79. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/threads/packet_handler_thread.py +0 -0
  80. {albibong-1.0.6 → albibong-1.1.0}/src/albibong/threads/sniffer_thread.py +0 -0
@@ -7,6 +7,8 @@ dump/
7
7
  log/
8
8
  pcap/
9
9
  .vscode
10
+ src/*.db
11
+ *.db
10
12
 
11
13
  ### macOS ###
12
14
  # General
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: albibong
3
- Version: 1.0.6
3
+ Version: 1.1.0
4
4
  Summary: A cross-platform Albion Online damage, fame, and dungeon tracker
5
5
  Project-URL: Homepage, https://github.com/imjangkar/albibong
6
6
  Project-URL: Issues, https://github.com/imjangkar/albibong/issues
@@ -10,6 +10,7 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.10
13
+ Requires-Dist: peewee==3.17.6
13
14
  Requires-Dist: pywebview==5.1
14
15
  Requires-Dist: scapy==2.5.0
15
16
  Requires-Dist: websockets==12.0
@@ -11,7 +11,8 @@ exclude = [
11
11
  "/tests",
12
12
  "/gui",
13
13
  "/readme_screenshots",
14
- "/README.md"
14
+ "/README.md",
15
+ "/*.db"
15
16
  ]
16
17
 
17
18
  [project.scripts]
@@ -19,7 +20,7 @@ albibong = "albibong:main"
19
20
 
20
21
  [project]
21
22
  name = "albibong"
22
- version = "1.0.6"
23
+ version = "1.1.0"
23
24
  authors = [
24
25
  { name="imjangkar", email="imjangkar@gmail.com" },
25
26
  ]
@@ -34,6 +35,7 @@ dependencies = [
34
35
  "scapy==2.5.0",
35
36
  "websockets==12.0",
36
37
  "pywebview==5.1",
38
+ "peewee==3.17.6",
37
39
  ]
38
40
 
39
41
  [project.urls]
@@ -0,0 +1,147 @@
1
+ import json
2
+ import os
3
+ import queue
4
+ import random
5
+ import socket
6
+ import sys
7
+ import uuid
8
+ from datetime import datetime, timedelta
9
+ from pathlib import Path
10
+ from time import sleep
11
+
12
+ import webview
13
+ from scapy.all import rdpcap
14
+
15
+ from albibong.classes.dungeon import Dungeon
16
+ from albibong.classes.location import Island
17
+ from albibong.classes.logger import Logger
18
+ from albibong.classes.packet_handler import PacketHandler
19
+ from albibong.models.models import db
20
+ from albibong.threads.http_server import HttpServerThread
21
+ from albibong.threads.packet_handler_thread import PacketHandlerThread
22
+ from albibong.threads.sniffer_thread import SnifferThread
23
+ from albibong.threads.websocket_server import get_ws_server
24
+
25
+ logger = Logger(__name__, stdout=True, log_to_file=False)
26
+ PORT = random.randrange(8500, 8999)
27
+
28
+ home_dir = os.path.expanduser("~")
29
+ DUNGEON_DB = f"{home_dir}/Albibong/list_dungeon.json"
30
+ SQLITE_DB = f"{home_dir}/Albibong/Albibong.db"
31
+
32
+
33
+ def read_pcap(path):
34
+ packet_handler = PacketHandler()
35
+ scapy_cap = rdpcap(path)
36
+ for packet in scapy_cap:
37
+ packet_handler.handle(packet)
38
+
39
+
40
+ def sniff(useWebview):
41
+ _sentinel = object()
42
+ packet_queue = queue.Queue()
43
+
44
+ p = SnifferThread(name="sniffer", out_queue=packet_queue, sentinel=_sentinel)
45
+
46
+ c = PacketHandlerThread(
47
+ name="packet_handler",
48
+ in_queue=packet_queue,
49
+ sentinel=_sentinel,
50
+ )
51
+
52
+ p.start()
53
+ c.start()
54
+
55
+ ws_server = get_ws_server()
56
+ ws_server.start()
57
+
58
+ def get_end_time(str, start_time):
59
+ timer = str.split(":")
60
+ seconds = int(timer[0]) * 3600 + int(timer[1]) * 60 + int(timer[2])
61
+ return timedelta(seconds=seconds) + start_time
62
+
63
+ if Path(SQLITE_DB).is_file() == False:
64
+ db.connect()
65
+ db.create_tables([Dungeon, Island])
66
+ if Path(DUNGEON_DB).is_file() == True:
67
+ # convert json db to sqlite db
68
+ json_data = json.load(open(DUNGEON_DB))
69
+
70
+ for dungeon in json_data:
71
+ start_time = datetime.strptime(
72
+ dungeon["date_time"], "%a %d %b %Y, %I:%M%p"
73
+ )
74
+ type_splitted = set(dungeon["type"].split("_"))
75
+
76
+ if "CORRUPTED" in type_splitted:
77
+ converted_type = "CORRUPTED DUNGEON"
78
+ elif "HARDCORE" in type_splitted:
79
+ converted_type = "HARDCORE EXPEDITION"
80
+ elif "10V10" in type_splitted:
81
+ converted_type = "HELLGATE 10V10"
82
+ elif "5V5" in type_splitted:
83
+ converted_type = "HELLGATE 5V5"
84
+ elif "2V2" in type_splitted:
85
+ converted_type = "HELLGATE 2V2"
86
+ elif "DUNGEON" in type_splitted:
87
+ converted_type = "STATIC DUNGEON"
88
+ elif "RANDOMDUNGEON" in type_splitted:
89
+ converted_type = "SPAWN DUNGEON"
90
+ Dungeon.create(
91
+ type=converted_type,
92
+ name=dungeon["name"],
93
+ tier=dungeon["tier"],
94
+ fame=dungeon["fame"],
95
+ silver=dungeon["silver"],
96
+ re_spec=dungeon["re_spec"],
97
+ start_time=start_time,
98
+ end_time=get_end_time(dungeon["time_elapsed"], start_time),
99
+ )
100
+
101
+ db.connect(reuse_if_open=True)
102
+
103
+ if useWebview:
104
+
105
+ sock = socket.socket()
106
+ sock.bind(("", 0))
107
+ port = sock.getsockname()[1]
108
+ sock.close()
109
+
110
+ http_server = HttpServerThread(name="http_server", port=port)
111
+ http_server.start()
112
+
113
+ window = webview.create_window(
114
+ "Albibong",
115
+ url=f"http://localhost:{port}/",
116
+ width=1280,
117
+ height=720,
118
+ zoomable=True,
119
+ )
120
+
121
+ def on_closing():
122
+ c.stop()
123
+ p.stop()
124
+ ws_server.stop()
125
+ http_server.stop()
126
+ db.close()
127
+
128
+ window.events.closing += on_closing
129
+
130
+ webview.start()
131
+ else:
132
+ try:
133
+ while True:
134
+ sleep(100)
135
+ except KeyboardInterrupt as e:
136
+ p.stop()
137
+ ws_server.stop()
138
+
139
+
140
+ def main(useWebview=True):
141
+ if len(sys.argv) > 1:
142
+ ws_server = get_ws_server()
143
+ ws_server.start()
144
+
145
+ read_pcap(sys.argv[1])
146
+ else:
147
+ sniff(useWebview)
@@ -0,0 +1,66 @@
1
+ import uuid
2
+ from datetime import datetime
3
+
4
+ from peewee import *
5
+ from playhouse.sqlite_ext import JSONField
6
+
7
+ from albibong.models.models import BaseModel
8
+
9
+ DIVISOR = 10000
10
+
11
+
12
+ class Dungeon(BaseModel):
13
+
14
+ id = UUIDField(unique=True, default=uuid.uuid4)
15
+ type = CharField()
16
+ name = CharField()
17
+ tier = IntegerField(default=0)
18
+ fame = FloatField(default=0)
19
+ silver = FloatField(default=0)
20
+ re_spec = FloatField(default=0)
21
+ start_time = DateTimeField(default=datetime.now)
22
+ end_time = DateTimeField(null=True, default=None)
23
+ meter = JSONField(default=dict)
24
+
25
+ @staticmethod
26
+ def serialize(dungeon):
27
+ return {
28
+ "id": str(dungeon.id),
29
+ "type": dungeon.type,
30
+ "name": dungeon.name,
31
+ "tier": dungeon.tier,
32
+ "fame": dungeon.fame,
33
+ "silver": dungeon.silver,
34
+ "re_spec": dungeon.re_spec,
35
+ "date_time": dungeon.start_time.strftime("%a %d %b %Y, %I:%M%p"),
36
+ "time_elapsed": (
37
+ str(dungeon.end_time - dungeon.start_time).split(".")[0]
38
+ if dungeon.end_time
39
+ else ""
40
+ ),
41
+ "meter": dungeon.meter,
42
+ }
43
+
44
+ def set_end_time(self):
45
+ self.end_time = datetime.now()
46
+
47
+ def update_fame(self, parameters):
48
+ fame = parameters[2] / DIVISOR
49
+ self.fame += fame
50
+
51
+ def update_re_spec(self, parameters):
52
+ re_spec = parameters[2] / DIVISOR
53
+ self.re_spec += re_spec
54
+
55
+ def update_loot(self, parameters):
56
+ if 3 in parameters and parameters[3] == True:
57
+ silver = parameters[5] / DIVISOR
58
+ self.silver += silver
59
+
60
+ def update_meter(self, party):
61
+ self.meter = party
62
+
63
+ @staticmethod
64
+ def get_all_dungeon():
65
+ query = Dungeon.select().order_by(Dungeon.start_time.desc())
66
+ return [Dungeon.serialize(dungeon) for dungeon in query]
@@ -30,6 +30,10 @@ from albibong.classes.event_handler.handle_operation_change_cluster import (
30
30
  handle_operation_change_cluster,
31
31
  )
32
32
  from albibong.classes.event_handler.handle_operation_join import handle_operation_join
33
+ from albibong.classes.event_handler.handle_operation_farmable_harvest import (
34
+ handle_operation_farmable_harvest,
35
+ handle_operation_farmable_finish_grown_item,
36
+ )
33
37
  from albibong.classes.event_handler.handle_operation_move import handle_operation_move
34
38
  from albibong.classes.world_data import WorldData
35
39
  from albibong.resources.EventCode import EventCode
@@ -84,6 +88,12 @@ class EventHandler:
84
88
  self.response_handler[OperationCode.CHANGE_CLUSTER.value] = (
85
89
  handle_operation_change_cluster
86
90
  )
91
+ self.response_handler[OperationCode.FARMABLE_FINISH_GROWN_ITEM.value] = (
92
+ handle_operation_farmable_finish_grown_item
93
+ )
94
+ self.response_handler[OperationCode.FARMABLE_HARVEST.value] = (
95
+ handle_operation_farmable_harvest
96
+ )
87
97
 
88
98
  def on_request(self, world_data: WorldData, parameters):
89
99
  if REQUEST_TYPE_PARAMETER not in parameters:
@@ -1,5 +1,5 @@
1
+ from albibong.classes.event_handler.world_data_utils import WorldDataUtils
1
2
  from albibong.classes.world_data import WorldData
2
- from albibong.threads.websocket_server import send_event
3
3
 
4
4
 
5
5
  def handle_event_character_equipment_changed(world_data: WorldData, parameters):
@@ -11,14 +11,6 @@ def handle_event_character_equipment_changed(world_data: WorldData, parameters):
11
11
  ]
12
12
  if char.username in world_data.party_members:
13
13
  char.update_equipment(parameters[2])
14
- ws_update_character_equipment(world_data)
14
+ WorldDataUtils.ws_update_damage_meter(world_data)
15
15
  else:
16
16
  world_data.change_equipment_log[parameters[0]] = parameters[2]
17
-
18
-
19
- def ws_update_character_equipment(world_data: WorldData):
20
- event = {
21
- "type": "update_dps",
22
- "payload": {"party_members": world_data.serialize_party_members()},
23
- }
24
- send_event(event)
@@ -1,21 +1,22 @@
1
1
  from uuid import UUID
2
+
3
+ from albibong.classes.event_handler.world_data_utils import WorldDataUtils
2
4
  from albibong.classes.world_data import WorldData
3
- from albibong.threads.websocket_server import send_event
4
5
 
5
6
 
6
7
  def handle_event_party_joined(world_data: WorldData, parameters):
7
8
  world_data.party_members = set(parameters[5])
8
- ws_update_party_member(world_data)
9
+ WorldDataUtils.ws_update_damage_meter(world_data)
9
10
 
10
11
 
11
12
  def handle_event_party_disbanded(world_data: WorldData, parameters):
12
13
  world_data.party_members = {world_data.me.username}
13
- ws_update_party_member(world_data)
14
+ WorldDataUtils.ws_update_damage_meter(world_data)
14
15
 
15
16
 
16
17
  def handle_event_party_player_joined(world_data: WorldData, parameters):
17
18
  world_data.party_members.add(parameters[2])
18
- ws_update_party_member(world_data)
19
+ WorldDataUtils.ws_update_damage_meter(world_data)
19
20
 
20
21
 
21
22
  def handle_event_party_player_left(world_data: WorldData, parameters):
@@ -27,12 +28,4 @@ def handle_event_party_player_left(world_data: WorldData, parameters):
27
28
  # self out party
28
29
  if name == world_data.me.username:
29
30
  world_data.party_members = {world_data.me.username}
30
- ws_update_party_member(world_data)
31
-
32
-
33
- def ws_update_party_member(world_data: WorldData):
34
- event = {
35
- "type": "update_dps",
36
- "payload": {"party_members": world_data.serialize_party_members()},
37
- }
38
- send_event(event)
31
+ WorldDataUtils.ws_update_damage_meter(world_data)
@@ -0,0 +1,38 @@
1
+ from albibong.classes.event_handler.world_data_utils import WorldDataUtils
2
+ from albibong.classes.location import Island, Location
3
+ from albibong.classes.world_data import WorldData
4
+ from albibong.threads.websocket_server import send_event
5
+
6
+
7
+ def handle_operation_change_cluster(world_data: WorldData, parameters):
8
+ world_data.change_equipment_log = {}
9
+
10
+ if type(world_data.current_map).__name__ == "Island":
11
+ WorldDataUtils.set_island_status(
12
+ current_island=world_data.current_map, parameters=parameters
13
+ )
14
+
15
+ if 1 in parameters:
16
+ check_map = Location.get_location_from_code(parameters[1])
17
+ map_type_splitted = set(check_map.type.split(" "))
18
+ WorldDataUtils.set_dungeon_status(world_data, check_map, map_type_splitted)
19
+
20
+ if "ISLAND" in map_type_splitted:
21
+ island = Island.get_island_from_code(parameters[1])
22
+ island.name = f"{parameters[2]}'s {island.name}"
23
+ world_data.current_map = island
24
+ elif "HIDEOUT" in map_type_splitted:
25
+ hideout = Location.get_location_from_code(parameters[1])
26
+ hideout.name = f"{parameters[2]}'s {hideout.name}"
27
+ world_data.current_map = hideout
28
+
29
+ elif 0 in parameters:
30
+ check_map = Location.get_location_from_code(parameters[0])
31
+ map_type_splitted = set(check_map.type.split(" "))
32
+ is_dungeon = WorldDataUtils.set_dungeon_status(
33
+ world_data, check_map, map_type_splitted
34
+ )
35
+ if is_dungeon == False:
36
+ world_data.current_map = Location.get_location_from_code(parameters[0])
37
+
38
+ WorldDataUtils.ws_update_location(world_data)
@@ -0,0 +1,17 @@
1
+ from albibong.classes.world_data import WorldData
2
+
3
+
4
+ def handle_operation_farmable_harvest(world_data: WorldData, parameters):
5
+ if type(world_data.current_map).__name__ == "Island":
6
+ for i in range(len(parameters[0])):
7
+ world_data.current_map.add_crop(
8
+ unique_name=parameters[0][i], quantity=parameters[1][i]
9
+ )
10
+
11
+
12
+ def handle_operation_farmable_finish_grown_item(world_data: WorldData, parameters):
13
+ if type(world_data.current_map).__name__ == "Island":
14
+ for i in range(len(parameters[0])):
15
+ world_data.current_map.add_animal(
16
+ unique_name=parameters[0][i], quantity=parameters[1][i]
17
+ )
@@ -7,10 +7,16 @@ from albibong.threads.websocket_server import send_event
7
7
 
8
8
 
9
9
  def handle_operation_join(world_data: WorldData, parameters):
10
+ # update relative id if character has initialized before
11
+ if world_data.me.username != "not initialized":
12
+ WorldDataUtils.convert_id_to_name(
13
+ world_data,
14
+ old_id=world_data.me.id,
15
+ new_id=parameters[0],
16
+ char=world_data.me,
17
+ )
18
+
10
19
  # set my character
11
- WorldDataUtils.convert_id_to_name(
12
- world_data, old_id=world_data.me.id, new_id=parameters[0], char=world_data.me
13
- )
14
20
  world_data.me.uuid = Utils.convert_int_arr_to_uuid(parameters[1])
15
21
  world_data.me.username = parameters[2]
16
22
  world_data.me.guild = parameters[57] if 57 in parameters else ""
@@ -30,15 +36,15 @@ def handle_operation_join(world_data: WorldData, parameters):
30
36
  # set map my character is currently in
31
37
  if parameters[8][0] == "@":
32
38
  area = parameters[8].split("@")
33
- if area[1] == "RANDOMDUNGEON":
39
+ if area[1] == "RANDOMDUNGEON" or area[1] == "MISTS":
34
40
  check_map = Location.get_location_from_code(area[1])
35
41
  WorldDataUtils.start_current_dungeon(
36
42
  world_data, type=check_map.type, name=check_map.name
37
43
  )
38
44
 
39
45
  ws_init_character(world_data)
40
- ws_update_location(world_data)
41
- ws_update_dps(world_data)
46
+ WorldDataUtils.ws_update_location(world_data)
47
+ WorldDataUtils.ws_update_damage_meter(world_data)
42
48
 
43
49
 
44
50
  def ws_init_character(world_data: WorldData):
@@ -53,26 +59,3 @@ def ws_init_character(world_data: WorldData):
53
59
  },
54
60
  }
55
61
  send_event(event)
56
-
57
-
58
- def ws_update_location(world_data: WorldData):
59
- event = {
60
- "type": "update_location",
61
- "payload": {
62
- "map": world_data.current_map.name if world_data.current_map else "None",
63
- "dungeon": (
64
- Dungeon.serialize(world_data.current_dungeon)
65
- if world_data.current_dungeon
66
- else None
67
- ),
68
- },
69
- }
70
- send_event(event)
71
-
72
-
73
- def ws_update_dps(world_data: WorldData):
74
- event = {
75
- "type": "update_dps",
76
- "payload": {"party_members": world_data.serialize_party_members()},
77
- }
78
- send_event(event)
@@ -0,0 +1,155 @@
1
+ import json
2
+ import os
3
+ from collections import deque
4
+ from datetime import datetime
5
+
6
+ from albibong.classes.character import Character
7
+ from albibong.classes.dungeon import Dungeon
8
+ from albibong.classes.location import Island, Location
9
+ from albibong.classes.utils import Utils
10
+ from albibong.classes.world_data import WorldData
11
+ from albibong.threads.websocket_server import send_event
12
+
13
+ LIST_DUNGEON = os.path.join(os.path.expanduser("~"), "Albibong/list_dungeon.json")
14
+ os.makedirs(os.path.dirname(LIST_DUNGEON), exist_ok=True)
15
+ LIST_ISLAND = os.path.join(os.path.expanduser("~"), "Albibong/list_island.json")
16
+ os.makedirs(os.path.dirname(LIST_ISLAND), exist_ok=True)
17
+
18
+
19
+ class WorldDataUtils:
20
+
21
+ def save_current_island(current_map: Island):
22
+ current_map.save(force_insert=True)
23
+ WorldDataUtils.ws_update_total_harvest_by_date(
24
+ Island.get_total_harvest_by_date()
25
+ )
26
+ WorldDataUtils.ws_update_island(Island.get_all_island())
27
+
28
+ def set_island_status(current_island: Island, parameters):
29
+ check_map = (
30
+ Location.get_location_from_code(parameters[1])
31
+ if 1 in parameters
32
+ else Location.get_location_from_code(parameters[0])
33
+ )
34
+ check_map_name = (
35
+ f"{parameters[2]}'s {check_map.name}" if 2 in parameters else check_map.name
36
+ )
37
+ if current_island.name != check_map_name:
38
+ if current_island.crops != {} or current_island.animals != {}:
39
+ WorldDataUtils.save_current_island(current_island)
40
+
41
+ def ws_update_total_harvest_by_date(payload):
42
+ event = {
43
+ "type": "update_total_harvest_by_date",
44
+ "payload": payload,
45
+ }
46
+ send_event(event)
47
+
48
+ def ws_update_island(list_island: list):
49
+ event = {
50
+ "type": "update_island",
51
+ "payload": {"list_island": list_island},
52
+ }
53
+ send_event(event)
54
+
55
+ def end_current_dungeon(world_data: WorldData):
56
+ if world_data.current_dungeon:
57
+ world_data.current_dungeon.set_end_time()
58
+ world_data.current_dungeon.update_meter(
59
+ world_data.serialize_party_members()
60
+ )
61
+ world_data.current_dungeon.save(force_insert=True)
62
+ WorldDataUtils.ws_update_dungeon(Dungeon.get_all_dungeon())
63
+ world_data.current_dungeon = None
64
+
65
+ def ws_update_dungeon(list_dungeon: list):
66
+ event = {
67
+ "type": "update_dungeon",
68
+ "payload": {"list_dungeon": list_dungeon},
69
+ }
70
+ send_event(event)
71
+
72
+ @staticmethod
73
+ def start_current_dungeon(world_data: WorldData, type: str, name: str):
74
+ if world_data.current_dungeon == None:
75
+ new_dungeon = Dungeon(type=type, name=name)
76
+ world_data.current_dungeon = new_dungeon
77
+
78
+ @staticmethod
79
+ def set_dungeon_status(
80
+ world_data: WorldData, check_map: Location, map_type_splitted: set
81
+ ):
82
+ if "EXPEDITION" in map_type_splitted or "HELLGATE" in map_type_splitted:
83
+ WorldDataUtils.start_current_dungeon(
84
+ world_data, type=check_map.type, name=check_map.name
85
+ )
86
+ elif "DUNGEON" in map_type_splitted:
87
+ WorldDataUtils.start_current_dungeon(
88
+ world_data,
89
+ type=check_map.type,
90
+ name=(
91
+ f"{check_map.name} at {world_data.current_map.name}"
92
+ if world_data.current_map
93
+ else check_map.name
94
+ ),
95
+ )
96
+ elif (
97
+ "EXPEDITION" not in map_type_splitted or "DUNGEON" not in map_type_splitted
98
+ ):
99
+ WorldDataUtils.end_current_dungeon(world_data)
100
+ return False
101
+
102
+ @staticmethod
103
+ def convert_id_to_name(world_data: WorldData, old_id, new_id, char: Character):
104
+ if old_id in world_data.char_id_to_username:
105
+ world_data.char_id_to_username.pop(old_id) # delete old relative id
106
+ char.id = new_id
107
+ world_data.char_id_to_username[char.id] = char.username # add new relative id
108
+
109
+ @staticmethod
110
+ def update_damage_or_heal(world_data: WorldData, target, inflictor, nominal):
111
+
112
+ if inflictor not in world_data.char_id_to_username:
113
+ # character not initialized yet
114
+ return
115
+
116
+ username = world_data.char_id_to_username[inflictor]
117
+
118
+ if username == "not initialized":
119
+ # self not initialized
120
+ return
121
+
122
+ char: Character = world_data.characters[username]
123
+
124
+ if nominal < 0:
125
+ if target == inflictor:
126
+ # suicide
127
+ return
128
+ char.update_damage_dealt(abs(nominal))
129
+ else:
130
+ char.update_heal_dealt(nominal)
131
+
132
+ WorldDataUtils.ws_update_damage_meter(world_data)
133
+
134
+ def ws_update_damage_meter(world_data: WorldData):
135
+ event = {
136
+ "type": "update_damage_meter",
137
+ "payload": {"party_members": world_data.serialize_party_members()},
138
+ }
139
+ send_event(event)
140
+
141
+ def ws_update_location(world_data: WorldData):
142
+ event = {
143
+ "type": "update_location",
144
+ "payload": {
145
+ "map": (
146
+ world_data.current_map.name if world_data.current_map else "None"
147
+ ),
148
+ "dungeon": (
149
+ world_data.current_dungeon.name
150
+ if world_data.current_dungeon
151
+ else "None"
152
+ ),
153
+ },
154
+ }
155
+ send_event(event)
@@ -1,9 +1,16 @@
1
1
  import json
2
2
  import os
3
3
 
4
- itemsJsonPath = os.path.join(os.path.dirname(__file__), "../resources/items.json")
5
- with open(itemsJsonPath) as json_file:
6
- item_data = json.load(json_file)
4
+ itemsByIdJsonPath = os.path.join(
5
+ os.path.dirname(__file__), "../resources/items_by_id.json"
6
+ )
7
+ itemsByUniqueNameJsonPath = os.path.join(
8
+ os.path.dirname(__file__), "../resources/items_by_unique_name.json"
9
+ )
10
+ with open(itemsByIdJsonPath) as json_file:
11
+ items_by_id = json.load(json_file)
12
+ with open(itemsByUniqueNameJsonPath) as json_file:
13
+ items_by_unique_name = json.load(json_file)
7
14
 
8
15
 
9
16
  class Item:
@@ -17,6 +24,7 @@ class Item:
17
24
  if self.unique_name != "None"
18
25
  else "/No Equipment.png"
19
26
  )
27
+ self.quantity = 0
20
28
 
21
29
  @staticmethod
22
30
  def serialize(item):
@@ -25,6 +33,7 @@ class Item:
25
33
  "name": item.name,
26
34
  "unique_name": item.unique_name,
27
35
  "image": item.image,
36
+ "quantity": item.quantity,
28
37
  }
29
38
 
30
39
  @staticmethod
@@ -36,6 +45,12 @@ class Item:
36
45
 
37
46
  @classmethod
38
47
  def get_item_from_code(cls, code: str):
39
- item = item_data[code]
48
+ item = items_by_id[code]
49
+
50
+ return cls(id=item["id"], name=item["name"], unique_name=item["unique_name"])
51
+
52
+ @classmethod
53
+ def get_item_from_unique_name(cls, unique_name: str):
54
+ item = items_by_unique_name[unique_name]
40
55
 
41
56
  return cls(id=item["id"], name=item["name"], unique_name=item["unique_name"])