Skip to content

Commit

Permalink
added 13.9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksandr Shyshatskyi committed Oct 9, 2024
1 parent 95c94d5 commit a4e6656
Show file tree
Hide file tree
Showing 101 changed files with 9,070 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<root>
<ClientMethods>
<recieveSseBonuses>
<Args>
<sseBonuses> ARRAY<of>SSE_BONUS</of> </sseBonuses>
</Args>
</recieveSseBonuses>
<recieveCreatedSseBonus>
<Args>
<sseBonus> SSE_BONUS </sseBonus>
</Args>
</recieveCreatedSseBonus>
<recieveDeletedSseBonus>
<Args>
<sseBonus> SSE_BONUS </sseBonus>
</Args>
</recieveDeletedSseBonus>
</ClientMethods>
<ofEntity>
<Account/>
</ofEntity>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
<StrategicActionsComponent/>
<EventHubComponent/>
<BattleCardsComponent/>
<SseBonusComponent/>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@
<setTrace>
<Arg>BOOL</Arg>
</setTrace>
<curVersion_13_7_0_8777660></curVersion_13_7_0_8777660>
<curVersion_13_8_0_8893005></curVersion_13_8_0_8893005>
<updateSSEProgress>
<Arg>STRING</Arg>
<VariableLengthHeaderSize>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2097,4 +2097,13 @@
</Properties>
<implementedBy>RestrictionsDef.converter</implementedBy>
</RESTRICTIONS_INFO>

<SSE_BONUS>
FIXED_DICT
<Properties>
<configID><Type>STRING</Type></configID>
<idTask><Type>STRING</Type></idTask>
<coef><Type>UINT8</Type></coef>
</Properties>
</SSE_BONUS>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,16 @@
<Arg>ARRAY<of>UINT64</of></Arg>
<Exposed/>
</cmdL1>

<cmdL1I1S2>
<Arg>UINT8</Arg>
<Arg>UINT8</Arg>
<Arg>ARRAY<of>UINT64</of></Arg>
<Arg> UINT64 </Arg>
<Arg> UNICODE_STRING </Arg>
<Arg> UNICODE_STRING </Arg>
<Exposed/>
</cmdL1I1S2>
</BaseMethods>

</root>
5 changes: 5 additions & 0 deletions replay_unpack/clients/wows/versions/13_9_0/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# coding=utf-8

from .battle_controller import BattleController

__all__ = ['BattleController']
240 changes: 240 additions & 0 deletions replay_unpack/clients/wows/versions/13_9_0/battle_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# coding=utf-8
import copy
import logging
import pickle

from replay_unpack.core import IBattleController
from replay_unpack.core.entity import Entity
from .constants import DamageStatsType, Category, TaskType, Status

try:
from .constants import DEATH_TYPES, SHIP_TYPE_BY_ID, SKILL_TYPE_ID_TO_NAME
except ImportError:
DEATH_TYPES = {}
from .players_info import PlayersInfo, PlayerType


class BattleController(IBattleController):

def __init__(self):
self._entities = {}
self._achievements = {}
self._ribbons = {}
self._players = PlayersInfo()
self._battle_result = None
self._damage_map = {}
self._shots_damage_map = {}
self._death_map = []
self._map = {}
self._player_id = None
self._arena_id = None
self.postBattleResult = None

self._dead_planes = {}

Entity.subscribe_method_call('Avatar', 'onBattleEnd', self.onBattleEnd)
Entity.subscribe_method_call('Avatar', 'onArenaStateReceived', self.onArenaStateReceived)
Entity.subscribe_method_call('Avatar', 'onGameRoomStateChanged', self.onPlayerInfoUpdate)
Entity.subscribe_method_call('Avatar', 'receiveVehicleDeath', self.receiveVehicleDeath)
# Entity.subscribe_method_call('Vehicle', 'setConsumables', self.onSetConsumable)
# Entity.subscribe_method_call('Vehicle', 'onRibbon', self.onRibbon)
Entity.subscribe_method_call('Avatar', 'onAchievementEarned', self.onAchievementEarned)
Entity.subscribe_method_call('Avatar', 'receiveDamageStat', self.receiveDamageStat)
Entity.subscribe_method_call('Avatar', 'receive_planeDeath', self.receive_planeDeath)
Entity.subscribe_method_call('Avatar', 'onNewPlayerSpawnedInBattle', self.onNewPlayerSpawnedInBattle)

Entity.subscribe_method_call('Vehicle', 'receiveDamagesOnShip', self.g_receiveDamagesOnShip)

def onSetConsumable(self, vehicle, blob):
print(pickle.loads(blob))

@property
def entities(self):
return self._entities

@property
def battle_logic(self):
return next(e for e in self._entities.values() if e.get_name() == 'BattleLogic')

def create_entity(self, entity: Entity):
self._entities[entity.id] = entity

def destroy_entity(self, entity: Entity):
self._entities.pop(entity.id)

def on_player_enter_world(self, entity_id: int):
self._player_id = entity_id

def get_info(self):

# use avatar id here for backward compatibility
avatar = next(entity for entity in self.entities.values() if entity.get_name() == 'Avatar')
self._ribbons[avatar.id] = {}
for ribbon_info in avatar.properties['client']['privateVehicleState']['ribbons']:
self._ribbons[avatar.id][ribbon_info['ribbonId']] = ribbon_info['count']

# adding killed planes data
players = copy.deepcopy(self._players.get_info())
for player in players.values():
player['planesCount'] = self._dead_planes.get(
player.get('shipId', 0), 0)

return dict(
achievements=self._achievements,
ribbons=self._ribbons,
players=players,
battle_result=self._battle_result,
damage_map=self._damage_map,
shots_damage_map=self._shots_damage_map,
death_map=self._death_map,
death_info=self._getDeathsInfo(),
map=self._map,
player_id=self._player_id,
control_points=self._getCapturePointsInfo(),
tasks=list(self._getTasksInfo()),
skills=dict(),
crew=dict(self.getCrewInformation()),
arena_id=self._arena_id,
post_battle=self.postBattleResult
)

def _getCrewInfo(self, vehicle):
learned_skills_packed = vehicle.properties['client']['crewModifiersCompactParams']['learnedSkills']

learned_skills = {}
for type_id, type_name in SHIP_TYPE_BY_ID.items():
if not learned_skills_packed[type_id]:
continue

learned_skills[type_name] = [
SKILL_TYPE_ID_TO_NAME.get(skill_id)
for skill_id in learned_skills_packed[type_id]
]

return {
'crew_id': vehicle.properties['client']['crewModifiersCompactParams']['paramsId'],
'learned_skills': learned_skills
}

def getCrewInformation(self):
for e in self.entities.values():
if e.get_name() != 'Vehicle':
continue
yield e.id, self._getCrewInfo(e)

def _getDeathsInfo(self):
deaths = {}
for killedVehicleId, fraggerVehicleId, typeDeath in self._death_map:
death_type = DEATH_TYPES.get(typeDeath)
if death_type is None:
logging.warning('Unknown death type %s', typeDeath)
continue

deaths[killedVehicleId] = {
'killer_id': fraggerVehicleId,
'icon': death_type['icon'],
'name': death_type['name'],
}
return deaths

def _getCapturePointsInfo(self):
return self.battle_logic.properties['client']['state'].get('controlPoints', [])

def _getTasksInfo(self):
tasks = self.battle_logic.properties['client']['state'].get('tasks', [])
for task in tasks:
if not task['showOnHUD']:
continue

yield {
"category": Category.names[task['category']],
"status": Status.names[task['status']],
"name": task['id'],
"type": TaskType.names[task['type']]
}

def onBattleEnd(self, avatar):
self._battle_result = dict(
winner_team_id=self.battle_logic.properties['client']['battleResult']['winnerTeamId'],
victory_type=self.battle_logic.properties['client']['battleResult']['finishReason'],
)

def onNewPlayerSpawnedInBattle(self, avatar, playersData, botsData, observersData):
self._players.create_or_update_players(
pickle.loads(playersData, encoding='bytes'),
PlayerType.PLAYER
)
self._players.create_or_update_players(
pickle.loads(botsData, encoding='bytes'),
PlayerType.BOT
)
self._players.create_or_update_players(
pickle.loads(observersData, encoding='bytes'),
PlayerType.OBSERVER
)

def onArenaStateReceived(self, avatar, arenaUniqueId, teamBuildTypeId, preBattlesInfo, playersStates, botsStates,
observersState, buildingsInfo):
self._arena_id = arenaUniqueId
self._players.create_or_update_players(
pickle.loads(playersStates, encoding='bytes'),
PlayerType.PLAYER
)
self._players.create_or_update_players(
pickle.loads(botsStates, encoding='bytes'),
PlayerType.BOT
)
self._players.create_or_update_players(
pickle.loads(observersState, encoding='bytes'),
PlayerType.OBSERVER
)

def onPlayerInfoUpdate(self, avatar, playersData, botsData, observersData):
self._players.create_or_update_players(
pickle.loads(playersData, encoding='bytes'),
PlayerType.PLAYER
)
self._players.create_or_update_players(
pickle.loads(botsData, encoding='bytes'),
PlayerType.BOT
)
self._players.create_or_update_players(
pickle.loads(observersData, encoding='bytes'),
PlayerType.OBSERVER
)

def receiveDamageStat(self, avatar, blob):
normalized = {}
for (type_, bool_), value in pickle.loads(blob).items():
# TODO: improve damage_map and list other damage types too
if bool_ != DamageStatsType.DAMAGE_STATS_ENEMY:
continue
normalized.setdefault(type_, {}).setdefault(bool_, 0)
normalized[type_][bool_] = value
self._damage_map.update(normalized)

def onAchievementEarned(self, avatar, avatar_id, achievement_id):
# also rearrange ids for backward compatibility
player = self._players.get_info()[avatar_id]
self._achievements.setdefault(player['avatarId'], {}).setdefault(achievement_id, 0)
self._achievements[player['avatarId']][achievement_id] += 1

def receiveVehicleDeath(self, avatar, killedVehicleId, fraggerVehicleId, typeDeath):
self._death_map.append((killedVehicleId, fraggerVehicleId, typeDeath))

def g_receiveDamagesOnShip(self, vehicle, damages):
for damage_info in damages:
self._shots_damage_map.setdefault(vehicle.id, {}).setdefault(damage_info['vehicleID'], 0)
self._shots_damage_map[vehicle.id][damage_info['vehicleID']] += damage_info['damage']

def receive_planeDeath(self, avatar, squadronID, planeIDs, reason, attackerId):
self._dead_planes.setdefault(attackerId, 0)
self._dead_planes[attackerId] += len(planeIDs)

@property
def map(self):
raise NotImplemented()

@map.setter
def map(self, value):
self._map = value.lstrip('spaces/')
Loading

0 comments on commit a4e6656

Please sign in to comment.