def check_update(logger: Logger, repo: str, rpath: str, last_update: float): if not can_check: logger.warning(f"cant check update of {repo} - {rpath}") key = f"{repo} - {rpath}" cache = _storage.data.setdefault('cache', dict()).setdefault(key, dict()) headers_ = headers.copy() headers_["If-None-Match"] = cache.setdefault('etag', '') try: q = get(f"{domain}/repos/{repo}/commits", {'path': rpath, 'page': '1', 'per_page': '1', }, headers=headers_) except Exception as e: logger.warning(f"cant check update of {repo} - {rpath}: {e}") else: if q.status_code != 304: data = q.json() if q.status_code != 200: logger.warning(q.json()["message"]) return cache['etag'] = q.headers['ETag'] new_timestamp = datetime.strptime(data[0]["commit"]["author"]["date"], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc).timestamp() cache['timestamp'] = int(new_timestamp) _storage.save() t = cache['timestamp'] if t > last_update: logger.warning(f"There is a update at {datetime.fromtimestamp(t)} on https://github.com/{repo}") else: logger.debug(f"{repo} - {rpath} is up to date") _storage.save()
def get_last_update(name: Optional[str], current_hash: str, logger: Logger): if name is None: data_dir = _storage.data.setdefault('core', dict()) else: data_dir = _storage.data.setdefault('plugins', dict()).setdefault(name, dict()) if 'time' not in data_dir or 'hash' not in data_dir or data_dir['hash'] != current_hash: logger.debug("last update time update") data_dir['time'] = time() data_dir['hash'] = current_hash _storage.save() return data_dir['time']
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from .Structs import ServerAddStatusEffect, NetworkEventBase _logger = Logger("XivNetwork/ProcessAddStatusEffect") size = sizeof(ServerAddStatusEffect) class AddStatusEffectEvent(NetworkEventBase): id = "network/add_status_effect" name = "network add status effect event" def __init__(self, raw_msg, msg_time): super().__init__(raw_msg, msg_time) self.actor_id = raw_msg.header.actor_id self.actor_id2 = raw_msg.actor_id self.current_hp = raw_msg.current_hp self.max_hp = raw_msg.max_hp self.current_mp = raw_msg.current_mp self.damage_shield = raw_msg.damage_shield self.effects = raw_msg.effects[:min(raw_msg.effect_count, 4)] def text(self): return f"update {hex(self.actor_id)[2:]};{self.current_hp}(+{self.damage_shield}%)/{self.max_hp};{self.current_mp}/10000;{[e.get_data() for e in self.effects]}" def get_event(msg_time: datetime,
from ctypes import * from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.memory.StructFactory import OffsetStruct from ..Structs import RecvNetworkEventBase ServerEventPlay = OffsetStruct( { 'target_id': c_uint, 'unk0': c_uint, 'event_id': c_ushort, 'category': c_ushort, }, 0x28) _logger = Logger("XivNetwork/ProcessServerEventPlay") size = sizeof(ServerEventPlay) class ServerEventPlayEvent(RecvNetworkEventBase): id = "network/recv_event_play" name = "network recv event play" def text(self): return f"event play {self.raw_msg.category}-{self.raw_msg.event_id} (target:{hex(self.raw_msg.target_id)})" def get_event(msg_time, header, raw_msg): if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return ServerEventPlayEvent(msg_time, header,
from math import degrees from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.memory.StructFactory import OffsetStruct from ..Structs import SendNetworkEventBase, Vector3 ClientPositionSet = OffsetStruct( { 'r': (c_float, 0), 'unk0': (c_ushort, 0x4), 'unk1': (c_ushort, 0x6), 'pos': (Vector3, 0x8), 'unk2': (c_uint, 0x14), }, 24) _logger = Logger("XivNetwork/ProcessClientPositionSet") size = sizeof(ClientPositionSet) class PositionSetEvent(SendNetworkEventBase): id = "network/position_set" name = "network send self position set" def __init__(self, msg_time, header, raw_msg): super().__init__(msg_time, header, raw_msg) self.new_r = raw_msg.r self.new_pos = raw_msg.pos def text(self): return "move to ({:.2F},{:.2F},{:.2F}) facing {:.2F}°".format( self.new_pos.x, self.new_pos.y, self.new_pos.z,
'duration': c_float, 'source_actor_id': c_uint, }) ServerAddStatusEffect = OffsetStruct({ 'related_action_sequence': c_uint, 'actor_id': c_uint, 'current_hp': c_uint, 'max_hp': c_uint, 'current_mp': c_ushort, 'unk0': c_ushort, 'damage_shield': c_ubyte, 'effect_count': c_ubyte, 'unk1': c_ushort, 'effects': ServerStatusEffectAddEntry * 4, # 'unk2': c_uint, }) _logger = Logger("XivNetwork/ProcessAddStatusEffect") size = sizeof(ServerAddStatusEffect) _logger.debug("size: %d" % size) def get_event(msg_time: datetime, header, raw_msg: bytearray) -> Optional[RecvNetworkEventBase]: if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return RecvAddStatusEffectEvent(msg_time, header, ServerAddStatusEffect.from_buffer(raw_msg))
@cached_property def item(self): if self.item_id: return item_sheet[self.item_id] ServerMarketBoardItemList = OffsetStruct({ 'items': MarketBoardItemEntry * 10, 'list_index_end': c_ubyte, 'list_index_start': c_ubyte, 'request_id': c_ushort, 'unk1': c_uint, }, 1528) _logger = Logger("XivNetwork/ProcessMarketBoardItemList") size = sizeof(ServerMarketBoardItemList) item_sheet = realm.game_data.get_sheet('Item') class ServerMarketBoardItemListEvent(RecvNetworkEventBase): id = "network/recv_market_board_item_list" name = "network recv market board item list" def __init__(self, msg_time, header, raw_msg): super().__init__(msg_time, header, raw_msg) self.item_id = self.raw_msg.items[0].item_id self.item_count = 0 for item in self.raw_msg.items: if item.item_id: self.item_count += 1
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from ..Structs import ServerActorGauge, RecvNetworkEventBase _logger = Logger("XivNetwork/ProcessActorGauge") size = sizeof(ServerActorGauge) class RecvActorGaugeEvent(RecvNetworkEventBase): id = "network/actor_gauge" name = "network actor gauge event" def __init__(self, msg_time, raw_msg): super().__init__(msg_time, raw_msg) self.buffer = bytearray(raw_msg.buffer) def text(self): return self.buffer.hex() def get_event(msg_time: datetime, raw_msg: bytearray) -> Optional[RecvNetworkEventBase]: if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return RecvActorGaugeEvent(msg_time, ServerActorGauge.from_buffer(raw_msg))
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from ..Structs import ServerActionEffect1, ServerActionEffect8, ServerActionEffect16 from ..Structs import ServerActionEffect24, ServerActionEffect32, ServerActionEffectHeader from ..Structs import ServerActionEffectDisplayType, RecvNetworkEventBase as EventBase _logger = Logger("XivNetwork/ProcessAbility") header_size = sizeof(ServerActionEffectHeader) a1_size = sizeof(ServerActionEffect1) a8_size = sizeof(ServerActionEffect8) a16_size = sizeof(ServerActionEffect16) a24_size = sizeof(ServerActionEffect24) a32_size = sizeof(ServerActionEffect32) def get_event1(msg_time: datetime, raw_msg: bytearray) -> Optional[EventBase]: if len(raw_msg) < a1_size: _logger.warning( f"message is too short to parse as ServerActionEffect1 {len(raw_msg)}/{a1_size} [{raw_msg.hex()}]" ) else: return get_event(msg_time, ServerActionEffect1.from_buffer(raw_msg), 1) def get_event8(msg_time: datetime, raw_msg: bytearray) -> Optional[EventBase]: if len(raw_msg) < a8_size:
from datetime import datetime, timezone from time import time from traceback import format_exc from typing import Optional from requests import get import urllib.request from urllib.error import HTTPError, URLError from socket import timeout from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.Storage import ModuleStorage, BASE_PATH domain = "https://api.github.com" _logger = Logger("CheckGitUpdater") _storage = ModuleStorage(BASE_PATH / "Update") headers = { 'User-Agent': 'FFxivPythonTrigger', } def test_url(url): try: return urllib.request.urlopen(url, timeout=3).getcode() == 200 except (HTTPError, URLError) as error: _logger.warning('Data of [%s] not retrieved because %s' % (url, error)) except timeout: _logger.warning('socket timed out - URL [%s]' % url) return False
from FFxivPythonTrigger.SaintCoinach import realm from FFxivPythonTrigger.Logger import Logger from .GetInteger import * from .Keys import * _logger = Logger("chat_log/messages") world_sheet = realm.game_data.get_sheet('World') item_sheet = realm.game_data.get_sheet('Item') territory_type_sheet = realm.game_data.get_sheet('TerritoryType') map_sheet = realm.game_data.get_sheet('Map') status_sheet = realm.game_data.get_sheet('Status') quest_sheet = realm.game_data.get_sheet('Quest') completion_sheet = realm.game_data.get_sheet('Completion') def extract_special_message(raw: bytearray): if raw.pop(0) != START_BYTE: raise Exception("Special Message decode, start byte mismatch") type_code = raw.pop(0) length = raw.pop(0) if len(raw) < length: raise Exception("Special Message length invalid") data = raw[:length] if data[-1] == END_BYTE: del data[-1] del raw[:length] return type_code, data def pack_special_message(type_code: int, data: bytearray = bytearray()): rtn = bytearray([START_BYTE, type_code, len(data) + 1])
'selling_count': c_ubyte, 'market': c_ubyte, 'class_job': c_ubyte, 'level': c_ubyte, 'sell_end_time': c_uint, 'mission_id': c_uint, 'adv_end_time': c_uint, 'reserved': c_ubyte, '_name': c_char * 39, }, 80, ['name'])): @property def name(self): return self._name.decode('utf-8', errors='ignore') _logger = Logger("XivNetwork/ProcessServerRetainerInformation") size = sizeof(ServerRetainerInformation) class ServerRetainerInformationEvent(RecvNetworkEventBase): id = "network/recv_retainer_info" name = "network recv retainer info" def text(self): return f"{self.raw_msg.name} {hex(self.raw_msg.retainer_id)}/{hex(self.raw_msg.server_id)}" if self.raw_msg.reserved else 'n/a' def get_event(msg_time, header, raw_msg): if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return
OffsetStruct( { 'container_sequence': c_uint, 'container_id': c_ushort, 'slot': c_ushort, 'count': c_uint, 'unk0': c_uint, 'item_id': c_uint, 'unk1': c_uint, 'unk2': c_uint, 'unk3': c_uint, }, 32)): pass _logger = Logger("XivNetwork/ProcessServerCurrencyCrystalInfo") size = sizeof(ServerCurrencyCrystalInfo) item_sheet = realm.game_data.get_sheet('Item') class ServerCurrencyCrystalInfoEvent(RecvNetworkEventBase): id = "network/recv_currency_crystal_info" name = "network recv currency crystal info" def __init__(self, msg_time, header, raw_msg): super().__init__(msg_time, header, raw_msg) self.container_id = self.raw_msg.container_id self.slot = self.raw_msg.slot self.count = self.raw_msg.count @cached_property
from ctypes import sizeof from FFxivPythonTrigger.Logger import Logger from ..Structs import SendNetworkEventBase, ClientEventAction, header_size _logger = Logger("XivNetwork/ProcessClientEventAction") size = sizeof(ClientEventAction) class ClientEventActionEvent(SendNetworkEventBase): id = "network/send_event_action" name = "network send event action" def text(self): return f"event action {self.raw_msg.category}-{self.raw_msg.event_id}" def get_event(msg_time, raw_msg): if len(raw_msg) < size + header_size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return ClientEventActionEvent( msg_time, ClientEventAction.from_buffer(raw_msg[header_size:]))
'param1': c_uint, 'param2': c_uint, 'param3': c_uint, 'param4': c_uint, 'padding1': c_uint, }) def dot_hot_event(msg_time, header, msg): return (HotEvent if msg.param2 == 4 else DotEvent if msg.param2 == 3 else UnknownDotHotEvent)(msg_time, header, msg) size = sizeof(ServerActorControl142) _logger = Logger("XivNetwork/ProcessActorControl142") _unknown_category = set() _unknown_dot_hot_type = set() _logger.debug("size: %d" % size) category_event_map = { 6: DeathEvent, 22: EffectUpdateEvent, 54: TargetableEvent, 35: TetherEvent, 5: JobChangeEvent, 21: EffectRemoveEvent, 23: dot_hot_event, }
# prev: 上一个技能名 # effect: 函数,获取buff层数/回合 e.g.effect("內静") ############### import re from itertools import chain from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.Storage import get_module_storage from FFxivPythonTrigger.MacroParser import Macro, MacroFinish from pathlib import Path from .. import Solver from ...simulator.Craft import CheckUnpass _logger = Logger("CraftMacroSolver") _storage = get_module_storage("MacroCraft") macro_dir = Path(__file__).parent / 'macros' macro_craft_tag_regex = re.compile( r"#CraftMacro:\[(?P<key>[^]]+)]:(?P<arg>[^\n]+)\n") macro_max_size = 100 macro_pairing = dict() macros = list() recipe_pair_id = dict() recipe_pair_name = dict() macro_file = "*.macro" class MacroOversize(Exception): pass
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from ..Structs import ServerActorControlCategory, ServerActorControl143, RecvNetworkEventBase _logger = Logger("XivNetwork/ProcessActorControl143") _unknown_category = set() _unknown_director_update = set() size = sizeof(ServerActorControl143) class InitialCommenceEvent(RecvNetworkEventBase): id = "network/actor_control/director_update/initial_commence" name = "network initial commence event" def __init__(self, msg_time, raw_msg): super().__init__(msg_time, raw_msg) self.content = raw_msg.param1 self.time = raw_msg.param3 def text(self): return f"{self.content}({self.time}s)" class RecommenceEvent(InitialCommenceEvent): id = "network/actor_control/director_update/recommence" name = "network recommence event"
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from .Structs import ServerUpdateHpMpTp, RecvNetworkEventBase _logger = Logger("XivNetwork/ProcessActorUpdateHpMpTp") size = sizeof(ServerUpdateHpMpTp) class RecvActorUpdateHpMpTpEvent(RecvNetworkEventBase): id = "network/actor_update_hp_mp_tp" name = "network actor update hp mp tp event" def __init__(self, raw_msg, msg_time): super().__init__(raw_msg, msg_time) self.actor_id = raw_msg.header.actor_id self.current_hp = raw_msg.current_hp self.current_mp = raw_msg.current_mp def text(self): return f"{hex(self.actor_id)[2:]} - {self.current_hp},{self.current_mp}" def get_event(msg_time: datetime, raw_msg: bytearray) -> Optional[RecvNetworkEventBase]: if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return
from FFxivPythonTrigger.memory import scan_address from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.Storage import get_module_storage from FFxivPythonTrigger.AddressManager import AddressManager _logger = Logger("XivMem/AddressManager") _storage = get_module_storage("XivMem") _am = AddressManager(_storage.data, _logger) ########## # actor table ########## actor_table_sig = "48 8d ?? ?? ?? ?? ?? e8 ?? ?? ?? ?? 48 8b ?? 48 8b ?? 48 8d ?? ?? ?? ?? ?? " \ "e8 ?? ?? ?? ?? 48 8d ?? ?? ?? ?? ?? ba ?? ?? ?? ?? e8 ?? ?? ?? ?? 89 2f" actor_table_addr = _am.get("actor table", scan_address, actor_table_sig, cmd_len=7) ########## # combat data ########## combo_state_sig = "f3 0f ?? ?? ?? ?? ?? ?? f3 0f ?? ?? ?? e8 ?? ?? ?? ?? 48 8b ?? 48 8b ?? 0f b7" combo_state_addr = _am.get("combo state", scan_address, combo_state_sig, cmd_len=8) skill_queue_sig = "44 89 2d ?? ?? ?? ?? f3 0f 11 05 ?? ?? ?? ??" skill_queue_addr = _am.get("skill queue", scan_address,
from ctypes import * from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.memory.StructFactory import OffsetStruct from ..Structs import RecvNetworkEventBase ServerEventFinish = OffsetStruct({ 'event_id': c_ushort, 'category': c_ushort, }, 0x10) _logger = Logger("XivNetwork/ProcessServerEventFinish") size = sizeof(ServerEventFinish) class ServerEventFinishEvent(RecvNetworkEventBase): id = "network/recv_event_finish" name = "network recv event finish" def text(self): return f"event finish {self.raw_msg.category}-{self.raw_msg.event_id}" def get_event(msg_time, header, raw_msg): if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return ServerEventFinishEvent(msg_time, header, ServerEventFinish.from_buffer(raw_msg))
from ctypes import sizeof from FFxivPythonTrigger.Logger import Logger from ..Structs import RecvNetworkEventBase, ServerEventStart, header_size _logger = Logger("XivNetwork/ProcessServerEventStart") size = sizeof(ServerEventStart) class ServerEventStartEvent(RecvNetworkEventBase): id = "network/recv_event_start" name = "network recv event start" def text(self): return f"event start {self.raw_msg.category}-{self.raw_msg.event_id} (target:{hex(self.raw_msg.target_id)})" def get_event(msg_time, raw_msg): if len(raw_msg) < size + header_size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return return ServerEventStartEvent( msg_time, ServerEventStart.from_buffer(raw_msg, header_size))
self.current = 0 class WaitTimeoutException(Exception): def __init__(self): super(WaitTimeoutException, self).__init__("Wait Timeout") def wait_until(statement: Callable[[], any], timeout: float = None, period: float = 0.1): temp = statement() start = time.perf_counter() while temp is None: if timeout is not None and time.perf_counter() - start >= timeout: raise WaitTimeoutException() time.sleep(period) temp = statement() return temp _err_logger = Logger("ErrorCatcher") def err_catch(func): def warper(*args, **kwargs): try: func(*args, **kwargs) except Exception: _err_logger.error(format_exc()) return warper
from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger import FFxiv_Version from ..Structs import RecvNetworkEventBase, ServerMessageHeader, header_size from .Opcodes import opcodes from . import AddStatusEffect, Ability, ActorCast, ActorControl142, StatusEffectList, ActorControl143, Ping from . import ActorControl144, ActorGauge, ActorUpdateHpMpTp, EventStart, EventPlay, EventFinish, CraftStatus _logger = Logger("XivNetwork/RecvProcessors") _processors = { 'StatusEffectList': StatusEffectList.get_event, 'StatusEffectList2': StatusEffectList.get_event2, 'BossStatusEffectList': StatusEffectList.get_eventB, 'Ability1': Ability.get_event1, 'Ability8': Ability.get_event8, 'Ability16': Ability.get_event16, 'Ability24': Ability.get_event24, 'Ability32': Ability.get_event32, 'ActorCast': ActorCast.get_event, 'AddStatusEffect': AddStatusEffect.get_event, 'ActorControl142': ActorControl142.get_event, 'ActorControl143': ActorControl143.get_event, 'ActorControl144': ActorControl144.get_event, 'UpdateHpMpTp': ActorUpdateHpMpTp.get_event, 'ActorGauge': ActorGauge.get_event, 'Ping': Ping.get_event, "EventStart": EventStart.get_event, "EventFinish": EventFinish.get_event, "EventPlay": EventPlay.get_event, "CraftStatus": CraftStatus.get_event,
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from .Structs import ServerActorCast,NetworkEventBase _logger = Logger("XivNetwork/ProcessActorCast") size = sizeof(ServerActorCast) class ActorCastEvent(NetworkEventBase): id = "network/actor_cast" name = "network actor cast event" def __init__(self, raw_msg, msg_time): super().__init__(raw_msg, msg_time) self.source_id = raw_msg.header.actor_id self.target_id = raw_msg.target_id self.action_id = raw_msg.action_id self.cast_time = raw_msg.cast_time def text(self): return f"{hex(self.source_id)[2:]} is casting {self.action_id} on {hex(self.target_id)[2:]} on {self.cast_time}s" def get_event(msg_time: datetime, raw_msg: bytearray) -> Optional[NetworkEventBase]: if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return
from ctypes import sizeof from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from .Structs import ServerStatusEffectList, ServerStatusEffectList2, ServerBossStatusEffectList, RecvNetworkEventBase _logger = Logger("XivNetwork/ProcessStatusEffectListEffect") size = sizeof(ServerStatusEffectList) size2 = sizeof(ServerStatusEffectList2) sizeB = sizeof(ServerBossStatusEffectList) class RecvStatusEffectListEvent(RecvNetworkEventBase): id = "network/status_effect_list" name = "network status effect list event" def __init__(self, raw_msg, msg_time): super().__init__(raw_msg, msg_time) self.actor_id = raw_msg.header.actor_id self.current_hp = raw_msg.current_hp self.max_hp = raw_msg.max_hp self.current_mp = raw_msg.current_mp self.max_mp = raw_msg.max_mp self.damage_shield = raw_msg.damage_shield self.levels = (raw_msg.level_1, raw_msg.level_2, raw_msg.level_3) self.effects = [e for e in raw_msg.effects if e.effect_id] def text(self): return f"update {hex(self.actor_id)[2:]};{self.current_hp}(+{self.damage_shield}%)/{self.max_hp};{self.current_mp}/{self.max_mp};{[e.get_data() for e in self.effects]}"
from FFxivPythonTrigger.memory import scan_pattern, StructFactory, write_bytes, read_memory from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.Storage import get_module_storage from FFxivPythonTrigger.AddressManager import AddressManager from FFxivPythonTrigger import FFxiv_Version, PluginBase, api from ctypes import c_ubyte, addressof """ patch code to skip cutscene in some zone command: @cutscene format: /e @cutscene [p(patch)/d(dispatch)] """ command = "@cutscene" _logger = Logger("CutsceneSkipper") _storage = get_module_storage("CutsceneSkipper") sig = "75 33 48 8B 0D ?? ?? ?? ?? BA ?? 00 00 00 48 83 C1 10 E8 ?? ?? ?? ?? 83 78" addr = AddressManager(_storage.data, _logger).get("addr", scan_pattern, sig) _storage.save() _ver_storage = _storage.data[FFxiv_Version] _code = read_memory( StructFactory.OffsetStruct({ "mark1": (c_ubyte * 2, 0), "mark2": (c_ubyte * 2, 0x1b) }), addr) def is_patched(): return _code.mark1[:] == [0x90, 0x90]
from ctypes import * from datetime import datetime from typing import Optional from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.memory.StructFactory import OffsetStruct from ..Structs import RecvNetworkEventBase _logger = Logger("XivNetwork/ProcessActorCast") ServerActorCast = OffsetStruct({ 'action_id': c_ushort, 'skill_type': c_ubyte, 'unk0': c_ubyte, 'unk1': c_uint, 'cast_time': c_float, 'target_id': c_uint, 'rotation': c_float, 'unk2': c_uint, 'x': c_ushort, 'y': c_ushort, 'z': c_ushort, 'unk3': c_ushort, }) size = sizeof(ServerActorCast) _logger.debug("size: %d" % size) class RecvActorCastEvent(RecvNetworkEventBase):
from ctypes import * from FFxivPythonTrigger.Logger import Logger from FFxivPythonTrigger.memory.StructFactory import OffsetStruct from ..Structs import SendNetworkEventBase ClientEventStart = OffsetStruct( { 'target_id': c_uint, 'unk0': c_uint, 'event_id': c_ushort, 'category': c_ushort, 'unk3': c_uint, }, 16) _logger = Logger("XivNetwork/ProcessClientEventStart") size = sizeof(ClientEventStart) class ClientEventStartEvent(SendNetworkEventBase): id = "network/send_event_start" name = "network send event start" def text(self): return f"event start {self.raw_msg.category}-{self.raw_msg.event_id} (target:{hex(self.raw_msg.target_id)})" def get_event(msg_time, header, raw_msg): if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return
class ClientTrigger( OffsetStruct( { 'param1': c_uint, 'param2': c_uint, 'param3': c_uint, 'param4': c_uint, 'param5': c_uint, 'param6': c_uint, 'param7': c_uint, 'param8': c_uint, }, 32)): pass _logger = Logger("XivNetwork/ProcessClientTrigger") size = sizeof(ClientTrigger) class ClientTriggerEvent(SendNetworkEventBase): id = "network/send_client_trigger" name = "network send client trigger" def text(self): return '|'.join(hex(v)[2:] for v in self.raw_msg.get_data().values()) def get_event(msg_time, header, raw_msg): if len(raw_msg) < size: _logger.warning("message is too short to parse:[%s]" % raw_msg.hex()) return
from ctypes import sizeof from threading import Lock from traceback import format_exc from typing import Optional from zlib import decompress, MAX_WBITS from socket import ntohl from datetime import datetime, timedelta, timezone from FFxivPythonTrigger.Logger import Logger from .Structs import FFXIVBundleHeader _logger = Logger("XivNetwork/BundleDecoder") MAGIC_NUMBER = bytearray(b"\x52\x52\xa0\x41") _encoding_error = False invalid_headers = set() min_datetime = datetime(1970, 1, 1).replace(tzinfo=timezone.utc).astimezone(tz=None) def ntohll(host): return ntohl(host & 0xFFFFFFFF) << 32 | ntohl((host >> 32) & 0xFFFFFFFF) header_size = sizeof(FFXIVBundleHeader)