SoprachevAK Posted August 1, 2021 Author Share Posted August 1, 2021 (edited) 56 minutes ago, yepev said: Я понимаю за что ты переживаешь, и именно привязанность данных к человеку как раз решает твою проблему. Ведь ему придётся завести огромное количество аккаунтов и айпишников, чтобы статистически значимо заспамить сервер в таком раскладе. Если привязать данные к аккаунтам через токены и сделать грамотные лимиты по приёму данных с каждого токена (на основе IP, rate limits итд), то проблема будет решена на корню, а не полумерами с детским псевдошифрованием. Делать привязку к аккаунту я точно не буду, потому что это крайне некомфортно для пользователей, а вот ratelimit думаю есть смысл, хотя бы на бои, потому что выстрелы могут быть с пулемётного танка по 400 за бой Ну и если делать ограничение по ip, нет никакого смысла делать привязку к аккаунту И всё таки, даже детская защита, способная отбить 95% хакеров (детей), лучше чем её отсутствие. Если оставлять только защиту по ip, то будет много желающих 400 раз за 8 минут поспамить. И получается, что делать защиту от детей надо, а делать защиту от хакеров бессмысленно, её всё равно найдут способ обойти, а я не банковский сервис делаю, и деньги не потеряю, так на кой тогда черт париться и усложнять пользователям жизнь Edited August 1, 2021 by SoprachevAK @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 1, 2021 Share Posted August 1, 2021 (edited) 3 часа назад, SoprachevAK сказал: Что если строить обратную траекторию, как параболу через опорную точку и место попадания, зная скорость снаряда и гравитацию это можно сделать. Так можно избавиться от векторов присланных сервером. Да, это вариант. Зная shotPos, velocityVect, gravityVect и HitPoint, по ним вычисляем аналитически реальный вектор снаряда в точке shotPos, после чего сравниваем его с исходным velocityVect. И всё. Больше ничего не требуется. Кода минимум. 3 часа назад, SoprachevAK сказал: Клиент отправляет только исходные данные Ну тогда это всем другое дело. Клиентская часть не важна, она получается обычный и ничего не содержит важного. А код функции расчета дисперсии с сервера с пояснениями по расчетной модели можно и в теме запостить. 2 часа назад, SoprachevAK сказал: пулемётного танка Я бы исключил пулеметные танки. По крайней мере на первых порах. Это песок там всегда мало кого интересовало что и куда там летит. 3 часа назад, SoprachevAK сказал: Ну насколько я понял, он как раз показал, что сервер НЕ оперирует распределение Релея, в отличие от новости на портале. Тк распределение Релея = 0 в центре круга, а в танках оно нулю не равно, что видно из его же графика ниже. Ну да, там модифицированная версия распределения Релея с набросом точек в центре, иначе пригорать будет. А в последние два года тем еще правили это место искусственно. Новость на портале ВГ выходило об этих правках. Edited August 1, 2021 by StranikS_Scan @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 1, 2021 Author Share Posted August 1, 2021 8 hours ago, StranikS_Scan said: Ну тогда это всем другое дело. Клиентская часть не важна, она получается обычный и ничего не содержит важного. А код функции расчета дисперсии с сервера с пояснениями по расчетной модели можно и в теме запостить. Думаю так и сделаю, когда допишу всё и появится время 8 hours ago, StranikS_Scan said: Я бы исключил пулеметные танки. В этом преимущество проекта, на сайте ты можешь посмотреть график по любой выборке, в том числе и исключая низкие уровни и САУ, как например настроено здесь https://wotstat.soprachev.com/analytics/61065e454c2ef064639d3004Ну а лично мне тут интересно управление высокими нагрузками, для этого конечно надо бд заполнить побольше @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 11, 2021 Author Share Posted August 11, 2021 (edited) Создал тему с планами на 2 версию мода, идеи предложения туда Ну а сюда тогда вопрос, можно ли хоть сколько нибудь достоверно получать информацию о толщине брони в точке по вектору, был раньше модик который показывал приведённю броню и шанс пробития, можно ли такое сейчас считать и можно ли например считать для своего танка, мол какая была вероятность что меня пробили бы Я видел мод для расчёта реальной точки попадания, но насколько я понял это уже после боя, а мне хотелось бы во время игры и чтоб не лагало Edited August 11, 2021 by SoprachevAK @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 12, 2021 Share Posted August 12, 2021 6 часов назад, SoprachevAK сказал: Я видел мод для расчёта реальной точки попадания, но насколько я понял это уже после боя, а мне хотелось бы во время игры и чтоб не лагало Нет, это в самой игре. А будет ли это реплей или реальный бой - не имеет значения. Лагать не будет, это очень слабая нагрузка. @ Quote Link to comment Short link Share on other sites More sharing options...
yepev Posted August 18, 2021 Share Posted August 18, 2021 @SoprachevAK а зачем две темы? Эта одна и для мода ещё одна. Можно было бы мод в этой же теме добавить в шапку. Было б удобнее, сразу можно прочитать обсуждение и понять что к чему и откуда пошло ) Да и название "Сбор и Анализ игровой статистики" вообще не привлекательное. Если б я заранее не знал, что за мод имеется ввиду, точно не кликнул бы на такое. Подумал бы, что очередной клон какого-нибудь мода про стату, а ведь на самом деле тут вообще по другому) Вот если б назвался "Мод для анализа подкруток", то это было б кликбейтно (в хорошем смысле). Спасибо, что открыл код. Крутой мув. Покопаюсь, когда будет время) @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 18, 2021 Author Share Posted August 18, 2021 14 hours ago, yepev said: @SoprachevAK а зачем две темы? Эта одна и для мода ещё одна. Можно было бы мод в этой же теме добавить в шапку. Было б удобнее, сразу можно прочитать обсуждение и понять что к чему и откуда пошло ) Да и название "Сбор и Анализ игровой статистики" вообще не привлекательное. Если б я заранее не знал, что за мод имеется ввиду, точно не кликнул бы на такое. Подумал бы, что очередной клон какого-нибудь мода про стату, а ведь на самом деле тут вообще по другому) Вот если б назвался "Мод для анализа подкруток", то это было б кликбейтно (в хорошем смысле). Спасибо, что открыл код. Крутой мув. Покопаюсь, когда будет время) Новая тема идеологически вообще о другом моде Тут обсуждались тупые вопросы мол "как сделать" анализ полёта снаряда, там общий сбор различных событий, где траектория снаряда лишь частный случай. Менять общую повестку на 3 странице я не сторонник, как по мне, лучше много маленьких тем, чем одна большая Уже накидал примерную основу нового мода, думаю на этой неделе начну делать бэк, а дальше добавление новых событий будет лишь вопросом питона в моде Название темы поменяю на кликбейт) @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 20, 2021 Author Share Posted August 20, 2021 On 8/1/2021 at 2:30 PM, StranikS_Scan said: HitPoint HipPoint правильно брать от сюда (мне кажется сомнительным тк тоже связано с трассером, но другого я не нашел)? И ещё назрел вопрос о результатах боя, насколько я нагуглил у картошки нет способа через API их получить и если человек закрыл игру не дождавшись результата, то я его никак не узнаю. А если не закрыл, то результат брать от сюда? мне надо в том числе и во время другого боя, и по завершению текущего, в игре ещё не тыкал, сейчас совсем времени нет в танки играть. @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 20, 2021 Share Posted August 20, 2021 2 часа назад, SoprachevAK сказал: HipPoint правильно брать от сюда (мне кажется сомнительным тк тоже связано с трассером, но другого я не нашел)? А другого способа нет. Только та информация, что прислал сервер. from Avatar import PlayerAvatar from Vehicle import Vehicle from VehicleEffects import DamageFromShotDecoder from Math import Matrix, Vector3 @BigWorld.new_overrideLib.registerEvent(Vehicle, 'showDamageFromShot', DEBUG_MAIN) def new_showDamageFromShot(self, attackerID, points, effectsIndex, *a, **k): #Это попадание в модель танка .... #Декодирование точки и вектора попадания выглядит так decodedPoints = DamageFromShotDecoder.decodeHitPoints(points, self.appearance.collisions) if decodedPoints: firstHitPoint = decodedPoints[0] compMatrix = Matrix(self.appearance.compoundModel.node(firstHitPoint.componentName)) firstHitDirLocal = firstHitPoint.matrix.applyToAxis(2) firstHitDir = compMatrix.applyVector(firstHitDirLocal) firstHitDir.normalise() firstHitPos = compMatrix.applyPoint(firstHitPoint.matrix.translation) @BigWorld.new_overrideLib.registerEvent(PlayerAvatar, 'explodeProjectile', DEBUG_MAIN) def new_explodeProjectile(self, shotID, effectsIndex, effectMaterialIndex, endPoint, velocityDir, *a, **k): #Попадание снаряда в землю #Декодирование не требуется, т.к. endPoint, velocityDir и есть точка и вектор попадания 2 часа назад, SoprachevAK сказал: И ещё назрел вопрос о результатах боя, насколько я нагуглил у картошки нет способа через API их получить и если человек закрыл игру не дождавшись результата, то я его никак не узнаю. А если не закрыл, то результат брать от сюда? мне надо в том числе и во время другого боя, и по завершению текущего, в игре ещё не тыкал, сейчас совсем времени нет в танки играть. Все верно, только если игра запущена и мод смог перехватить onBattleResultsReceived. Ниже древний пример, когда-то давно делал мод, что-то может изменилось, но не принципиально. import BigWorld __version__ = 'V1.1 P2.7 W0.9.10 26.10.2015' __author__ = 'StranikS_Scan' # Protected code ----------------------------------------------------------------- # WPG-mode ---------------------------------------------------------------- from os import path import BattleReplay from gui.shared.utils.HangarSpace import g_hangarSpace from gui import SystemMessages from PlayerEvents import g_playerEvents import constants from gui.battle_control.arena_info import getArenaGuiTypeLabel from helpers import isPlayerAccount, getFullClientVersion from ConnectionManager import connectionManager from datetime import datetime from functools import partial def WPG_PrintMessage(msg, INF): if len(msg)>0: if g_hangarSpace is not None and g_hangarSpace.inited: msg = '<textformat><font color="#E2D2A2" size="15"><b>WPG:</b></font><br><br>' + \ ('<font color="#E2D2A2" size="14">%s</font></textformat>' % msg) SystemMessages.pushMessage(msg, INF) else: BigWorld.callback(1.0, partial(WPG_PrintMessage, msg, INF)) class WPG_ResultDump(object): def __init__(self): g_playerEvents.onAccountBecomePlayer += self.__onAccountBecomePlayer g_playerEvents.onAvatarReady += self.__onAvatarReady g_playerEvents.onBattleResultsReceived += self.__onBattleResultsReceived self.Info = {} self.LogPath = './wpg_log' def __onAccountBecomePlayer(self): if not isPlayerAccount(): return else: player = BigWorld.player() if player.databaseID is None: BigWorld.callback(0.1, self.__onAccountBecomePlayer) else: self.Info['PlayerID'] = player.databaseID return def __onAvatarReady(self): def getBattleCount(accountDBID): import urllib2 import json request = 'http://api.worldoftanks.ru/wot/account/info/?application_id=demo&fields=statistics.all&account_id=%s' % accountDBID data = json.loads(urllib2.urlopen(request).read())['data'] player = data.get(str(accountDBID), None) if player != None: return int(player['statistics']['all']['battles']) return -1 if not BattleReplay.isPlaying() or 'PlayerID' in self.Info: player = BigWorld.player() self.Info['PlayerName'] = player.name self.Info['BattleNo'] = getBattleCount(self.Info['PlayerID']) + 1 self.Info['PlayerVehicleID'] = player.playerVehicleID self.Info['VehicleName'] = BigWorld.entities[player.playerVehicleID].typeDescriptor.name.replace(':', '-') arenaName = player.arena.arenaType.geometry i = arenaName.find('/') if i != -1: arenaName = arenaName[i+1:] self.Info['MapName'] = '%s(%s)' % (player.arena.arenaType.name, arenaName) self.Info['BattleType'] = getArenaGuiTypeLabel() self.Info['BattleLevel'] = player.arena.extraData.get('battleLevel') now = datetime.now() self.Info['Date'] = '%02d.%02d.%04d %02d:%02d:%02d' % (now.day, now.month, now.year, now.hour, now.minute, now.second) self.Info['ServerName'] = connectionManager.serverUserName self.Info['RegionCode'] = constants.AUTH_REALM self.Info['ClientVersion'] = getFullClientVersion() return def __onBattleResultsReceived(self, isPlayerVehicle, results): if not BattleReplay.isPlaying(): import json, copy from random import randint from struct import pack from os import path, mkdir if isPlayerVehicle and 'PlayerID' in self.Info: try: self.Info['BattleStatistics'] = BigWorld.player().arena.statistics vehicles = {} for k, v in BigWorld.player().arena.vehicles.iteritems(): vehicle = copy.copy(v) vehicle['vehicleType'] = v['vehicleType'].name if v['vehicleType'] is not None else '' del vehicle['forbidInBattleInvitations'] del vehicle['prebattleID'] del vehicle['isPrebattleCreator'] del vehicle['isAvatarReady'] del vehicle['potapovQuestIDs'] del vehicle['igrType'] del vehicle['events'] vehicles[k] = vehicle self.Info['ArenaVehiclesInfo'] = vehicles modifiedResults = copy.deepcopy(results) allPlayersVehicles = modifiedResults.get('vehicles', None) if allPlayersVehicles is not None: for playerVehicles in allPlayersVehicles.itervalues(): for vehicle in playerVehicles: vehicle['damageEventList'] = None personals = modifiedResults.get('personal', None) if personals is not None: for personal in personals.itervalues(): for field in ('damageEventList', 'xpReplay', 'creditsReplay', 'tmenXPReplay', 'fortResourceReplay', 'goldReplay', 'freeXPReplay'): personal[field] = None details = personal.pop('details', None) if details is not None: modifiedDetails = dict() for key, value in details.iteritems(): modifiedDetails[str(key)] = value personal['details'] = modifiedDetails self.Info['BattleResults'] = modifiedResults dump = [] dump.append('========================================================') dump.append(' WPG - BATTLE RESULT ') dump.append(" DON'T REWRITE OR MODIFIED THIS FILE ") dump.append('========================================================') dump.append('') dump.append('PlayerName: %s' % self.Info['PlayerName']) dump.append('PlayerID: %s' % self.Info['PlayerID']) dump.append('BattleNo: %s' % self.Info['BattleNo']) dump.append('') dump.append('PlayerVehicleID: %s' % self.Info['PlayerVehicleID']) dump.append('VehicleName: %s' % self.Info['VehicleName']) dump.append('MapName: %s' % self.Info['MapName']) dump.append('BattleType: %s' % self.Info['BattleType']) dump.append('BattleLevel: %s' % self.Info['BattleLevel']) dump.append('') dump.append('Date: %s' % self.Info['Date']) dump.append('ServerName: %s' % self.Info['ServerName']) dump.append('RegionCode: %s' % self.Info['RegionCode']) dump.append('') dump.append('ClientVersion: %s' % self.Info['ClientVersion']) dump.append('WPGVersion: %s' % __version__) dump.append('') dump.append('BattleStatistics:') dump.append(json.dumps(self.Info['BattleStatistics'], sort_keys=True).replace(',',',\n')) dump.append('') dump.append('ArenaVehiclesInfo:') dump.append(json.dumps(self.Info['ArenaVehiclesInfo'], indent=2, sort_keys=True)) dump.append('') dump.append('BattleResults:') dump.append(json.dumps(self.Info['BattleResults'], indent=2, sort_keys=True)) dump.append('') dump.append('') dump = '\n'.join(dump) self.LogFile = '%s_%d_%s.wpgr' % (self.Info['PlayerName'], self.Info['BattleNo'], self.Info['Date'].replace('.','').replace(':','').replace(' ','_')) except Exception as E: WPG_PrintMessage('Work was interrupted by the error!\n\n%s' % E, SystemMessages.SM_TYPE.Error) else: try: if not path.exists(self.LogPath): mkdir(self.LogPath) with open(self.LogPath+'/'+self.LogFile,'w') as f: f.write(dump) except Exception as E: WPG_PrintMessage('Was unable to save the wpgr-file!\n\n%s' % E, SystemMessages.SM_TYPE.Error) else: WPG_PrintMessage('Battle result saved in %s' % self.LogFile, SystemMessages.SM_TYPE.GameGreeting) self.Info = {} return __WPG_ResultDump = WPG_ResultDump() WPG_PrintMessage('- Statistics mode loaded successfully;\n'+\ '- Do not leave the battle before its completion;\n'+\ '- Files with results will be saved as \'%s/*.wpgr\'.' % __WPG_ResultDump.LogPath.replace('.',''),\ SystemMessages.SM_TYPE.GameGreeting) 1 @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 24, 2021 Author Share Posted August 24, 2021 (edited) On 8/20/2021 at 6:06 AM, StranikS_Scan said: А другого способа нет. Только та информация, что прислал сервер. from Avatar import PlayerAvatar from Vehicle import Vehicle from VehicleEffects import DamageFromShotDecoder from Math import Matrix, Vector3 @BigWorld.new_overrideLib.registerEvent(Vehicle, 'showDamageFromShot', DEBUG_MAIN) def new_showDamageFromShot(self, attackerID, points, effectsIndex, *a, **k): #Это попадание в модель танка .... #Декодирование точки и вектора попадания выглядит так decodedPoints = DamageFromShotDecoder.decodeHitPoints(points, self.appearance.collisions) if decodedPoints: firstHitPoint = decodedPoints[0] compMatrix = Matrix(self.appearance.compoundModel.node(firstHitPoint.componentName)) firstHitDirLocal = firstHitPoint.matrix.applyToAxis(2) firstHitDir = compMatrix.applyVector(firstHitDirLocal) firstHitDir.normalise() firstHitPos = compMatrix.applyPoint(firstHitPoint.matrix.translation) @BigWorld.new_overrideLib.registerEvent(PlayerAvatar, 'explodeProjectile', DEBUG_MAIN) def new_explodeProjectile(self, shotID, effectsIndex, effectMaterialIndex, endPoint, velocityDir, *a, **k): #Попадание снаряда в землю #Декодирование не требуется, т.к. endPoint, velocityDir и есть точка и вектор попадания Все верно, только если игра запущена и мод смог перехватить onBattleResultsReceived. Ниже древний пример, когда-то давно делал мод, что-то может изменилось, но не принципиально. import BigWorld __version__ = 'V1.1 P2.7 W0.9.10 26.10.2015' __author__ = 'StranikS_Scan' # Protected code ----------------------------------------------------------------- # WPG-mode ---------------------------------------------------------------- from os import path import BattleReplay from gui.shared.utils.HangarSpace import g_hangarSpace from gui import SystemMessages from PlayerEvents import g_playerEvents import constants from gui.battle_control.arena_info import getArenaGuiTypeLabel from helpers import isPlayerAccount, getFullClientVersion from ConnectionManager import connectionManager from datetime import datetime from functools import partial def WPG_PrintMessage(msg, INF): if len(msg)>0: if g_hangarSpace is not None and g_hangarSpace.inited: msg = '<textformat><font color="#E2D2A2" size="15"><b>WPG:</b></font><br><br>' + \ ('<font color="#E2D2A2" size="14">%s</font></textformat>' % msg) SystemMessages.pushMessage(msg, INF) else: BigWorld.callback(1.0, partial(WPG_PrintMessage, msg, INF)) class WPG_ResultDump(object): def __init__(self): g_playerEvents.onAccountBecomePlayer += self.__onAccountBecomePlayer g_playerEvents.onAvatarReady += self.__onAvatarReady g_playerEvents.onBattleResultsReceived += self.__onBattleResultsReceived self.Info = {} self.LogPath = './wpg_log' def __onAccountBecomePlayer(self): if not isPlayerAccount(): return else: player = BigWorld.player() if player.databaseID is None: BigWorld.callback(0.1, self.__onAccountBecomePlayer) else: self.Info['PlayerID'] = player.databaseID return def __onAvatarReady(self): def getBattleCount(accountDBID): import urllib2 import json request = 'http://api.worldoftanks.ru/wot/account/info/?application_id=demo&fields=statistics.all&account_id=%s' % accountDBID data = json.loads(urllib2.urlopen(request).read())['data'] player = data.get(str(accountDBID), None) if player != None: return int(player['statistics']['all']['battles']) return -1 if not BattleReplay.isPlaying() or 'PlayerID' in self.Info: player = BigWorld.player() self.Info['PlayerName'] = player.name self.Info['BattleNo'] = getBattleCount(self.Info['PlayerID']) + 1 self.Info['PlayerVehicleID'] = player.playerVehicleID self.Info['VehicleName'] = BigWorld.entities[player.playerVehicleID].typeDescriptor.name.replace(':', '-') arenaName = player.arena.arenaType.geometry i = arenaName.find('/') if i != -1: arenaName = arenaName[i+1:] self.Info['MapName'] = '%s(%s)' % (player.arena.arenaType.name, arenaName) self.Info['BattleType'] = getArenaGuiTypeLabel() self.Info['BattleLevel'] = player.arena.extraData.get('battleLevel') now = datetime.now() self.Info['Date'] = '%02d.%02d.%04d %02d:%02d:%02d' % (now.day, now.month, now.year, now.hour, now.minute, now.second) self.Info['ServerName'] = connectionManager.serverUserName self.Info['RegionCode'] = constants.AUTH_REALM self.Info['ClientVersion'] = getFullClientVersion() return def __onBattleResultsReceived(self, isPlayerVehicle, results): if not BattleReplay.isPlaying(): import json, copy from random import randint from struct import pack from os import path, mkdir if isPlayerVehicle and 'PlayerID' in self.Info: try: self.Info['BattleStatistics'] = BigWorld.player().arena.statistics vehicles = {} for k, v in BigWorld.player().arena.vehicles.iteritems(): vehicle = copy.copy(v) vehicle['vehicleType'] = v['vehicleType'].name if v['vehicleType'] is not None else '' del vehicle['forbidInBattleInvitations'] del vehicle['prebattleID'] del vehicle['isPrebattleCreator'] del vehicle['isAvatarReady'] del vehicle['potapovQuestIDs'] del vehicle['igrType'] del vehicle['events'] vehicles[k] = vehicle self.Info['ArenaVehiclesInfo'] = vehicles modifiedResults = copy.deepcopy(results) allPlayersVehicles = modifiedResults.get('vehicles', None) if allPlayersVehicles is not None: for playerVehicles in allPlayersVehicles.itervalues(): for vehicle in playerVehicles: vehicle['damageEventList'] = None personals = modifiedResults.get('personal', None) if personals is not None: for personal in personals.itervalues(): for field in ('damageEventList', 'xpReplay', 'creditsReplay', 'tmenXPReplay', 'fortResourceReplay', 'goldReplay', 'freeXPReplay'): personal[field] = None details = personal.pop('details', None) if details is not None: modifiedDetails = dict() for key, value in details.iteritems(): modifiedDetails[str(key)] = value personal['details'] = modifiedDetails self.Info['BattleResults'] = modifiedResults dump = [] dump.append('========================================================') dump.append(' WPG - BATTLE RESULT ') dump.append(" DON'T REWRITE OR MODIFIED THIS FILE ") dump.append('========================================================') dump.append('') dump.append('PlayerName: %s' % self.Info['PlayerName']) dump.append('PlayerID: %s' % self.Info['PlayerID']) dump.append('BattleNo: %s' % self.Info['BattleNo']) dump.append('') dump.append('PlayerVehicleID: %s' % self.Info['PlayerVehicleID']) dump.append('VehicleName: %s' % self.Info['VehicleName']) dump.append('MapName: %s' % self.Info['MapName']) dump.append('BattleType: %s' % self.Info['BattleType']) dump.append('BattleLevel: %s' % self.Info['BattleLevel']) dump.append('') dump.append('Date: %s' % self.Info['Date']) dump.append('ServerName: %s' % self.Info['ServerName']) dump.append('RegionCode: %s' % self.Info['RegionCode']) dump.append('') dump.append('ClientVersion: %s' % self.Info['ClientVersion']) dump.append('WPGVersion: %s' % __version__) dump.append('') dump.append('BattleStatistics:') dump.append(json.dumps(self.Info['BattleStatistics'], sort_keys=True).replace(',',',\n')) dump.append('') dump.append('ArenaVehiclesInfo:') dump.append(json.dumps(self.Info['ArenaVehiclesInfo'], indent=2, sort_keys=True)) dump.append('') dump.append('BattleResults:') dump.append(json.dumps(self.Info['BattleResults'], indent=2, sort_keys=True)) dump.append('') dump.append('') dump = '\n'.join(dump) self.LogFile = '%s_%d_%s.wpgr' % (self.Info['PlayerName'], self.Info['BattleNo'], self.Info['Date'].replace('.','').replace(':','').replace(' ','_')) except Exception as E: WPG_PrintMessage('Work was interrupted by the error!\n\n%s' % E, SystemMessages.SM_TYPE.Error) else: try: if not path.exists(self.LogPath): mkdir(self.LogPath) with open(self.LogPath+'/'+self.LogFile,'w') as f: f.write(dump) except Exception as E: WPG_PrintMessage('Was unable to save the wpgr-file!\n\n%s' % E, SystemMessages.SM_TYPE.Error) else: WPG_PrintMessage('Battle result saved in %s' % self.LogFile, SystemMessages.SM_TYPE.GameGreeting) self.Info = {} return __WPG_ResultDump = WPG_ResultDump() WPG_PrintMessage('- Statistics mode loaded successfully;\n'+\ '- Do not leave the battle before its completion;\n'+\ '- Files with results will be saved as \'%s/*.wpgr\'.' % __WPG_ResultDump.LogPath.replace('.',''),\ SystemMessages.SM_TYPE.GameGreeting) Всё ок, всё работает. Думаю правильно, единственный вопрос остаётся, что делать с выстрелом в скайбокс или на расстояние дальше дистанции стрельбы Там нет ни взрыва ни урона Возможно такая ситуация будет ещё с выстрелом по незамеченному противнику, там вроде бы снаряд просто пропадает, но ещё не тестил Вообще я немного сомневаюсь в использование такого способа, он конечно точнее с точки зрения механик, однако получаются разные способ расчёта для разных типов попаданий Например попадание по танку может отправляться более точно, чем по рельефу, и получится что точность попаданий будет отличаться от точности промахов, а это нехилый камень в огород отсутствия подкрутки Я конечно буду собирать всё и можно будет сравнить, но как показывает практика, если не решить сразу, не решится никогда Edited August 24, 2021 by SoprachevAK @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 24, 2021 Share Posted August 24, 2021 3 часа назад, SoprachevAK сказал: с выстрелом в скайбокс или на расстояние дальше дистанции стрельбы Их нужно игнорировать. Они бессмысленны. 3 часа назад, SoprachevAK сказал: Возможно такая ситуация будет ещё с выстрелом по незамеченному противнику, там вроде бы снаряд просто пропадает, но ещё не тестил Снаряд пропадает. Надо тоже игнорировать. 3 часа назад, SoprachevAK сказал: Например попадание по танку может отправляться более точно, чем по рельефу, и получится что точность попаданий будет отличаться от точности промахов, а это нехилый камень в огород отсутствия подкрутки Ну должно быть три типа выстрела: - в небо, перелёт, пропал, пулемётные танки: эти игнорируем - попадание в ландшафт: грузим на сервер с пометкой вида - попадание в коллижен: грузим на сервер с пометкой вида @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 24, 2021 Author Share Posted August 24, 2021 9 hours ago, StranikS_Scan said: Их нужно игнорировать. Они бессмысленны. Они являются выстрелами, траекторию которых определил рандом. Если не учитывать часть выстрелов, в итоговом распределение получится ошибка выжившего. Понятное дело, что таких выстрелов мало, и на распределение они повлияют не сильно, но например на алгоритмы анализа случайных последовательностей, повлиять могут. Буду собирать и трассёры и точки попаданий если такие есть. @ Quote Link to comment Short link Share on other sites More sharing options...
KPOT2338 Posted August 24, 2021 Share Posted August 24, 2021 А разве игнор перелётов не даст смещение средней точки попадания вниз? Как раз получится доказательство, что "убить землю" больше шансов @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 24, 2021 Share Posted August 24, 2021 1 час назад, SoprachevAK сказал: Они являются выстрелами, траекторию которых определил рандом. Если не учитывать часть выстрелов, в итоговом распределение получится ошибка выжившего. Понятное дело, что таких выстрелов мало, и на распределение они повлияют не сильно, но например на алгоритмы анализа случайных последовательностей, повлиять могут. Буду собирать и трассёры и точки попаданий если такие есть. И как результат забьешь БД мусором, а потом получишь интегральные показатели, которые ничего не показывают. Качество анализа напрямую зависит от качества исходных данных. 8 минут назад, KPOT2338 сказал: А разве игнор перелётов не даст смещение средней точки попадания вниз? Как раз получится доказательство, что "убить землю" больше шансов Тут не все так однозначно. Ведь качество собранного материала, а точнее точность с которой определен разброс выстрела модом очень разнится в зависимости от ситуации. @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 24, 2021 Author Share Posted August 24, 2021 45 minutes ago, StranikS_Scan said: И как результат забьешь БД мусором Могу позволить, новая версия использует колоночную СУБД (увеличение количества столбцов не влияет на производительность) 47 minutes ago, StranikS_Scan said: интегральные показатели, которые ничего не показывают Спорно. Есть методы нуждающиеся в полной исходной последовательности. Тае же нашел свой университетский учебник, где 70 страниц посвящено анализу псевдослучайных последовательностей. Со 118 по 200 страницы, лично я в универе по нему делал тесты Кнута, крайне неприятное занятие, но может кому будет интересно глянуть Иванов М.А., Чугунков И.В._Теория, применение и оценка качества генераторов псевдослучайных последовательностей.pdf @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 25, 2021 Share Posted August 25, 2021 10 часов назад, SoprachevAK сказал: Спорно. Есть методы нуждающиеся в полной исходной последовательности. Дык о какой полноте может идти речь, если выборка изначально неполна и субъективна? Ты же собираешь не первичные данные, типа чисел на выходе ГПСЧ, а координаты попадания снарядов. У тебя изначально нет и не будет никакой "полной исходной последовательности", только её отголоски. 1 @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 25, 2021 Author Share Posted August 25, 2021 8 hours ago, StranikS_Scan said: Дык о какой полноте может идти речь, если выборка изначально неполна и субъективна? Ты же собираешь не первичные данные, типа чисел на выходе ГПСЧ, а координаты попадания снарядов. У тебя изначально нет и не будет никакой "полной исходной последовательности", только её отголоски. Эти методы дают на выходе некое число, характеризующие качество случайности, ну и ожидается что это качество будет как минимум одинаковым у всех. Игроки ожидают случайное поведение рандома лично для себя, и именно его и стоит проверять. @ Quote Link to comment Short link Share on other sites More sharing options...
SoprachevAK Posted August 25, 2021 Author Share Posted August 25, 2021 On 8/20/2021 at 6:06 AM, StranikS_Scan said: мод смог перехватить onBattleResultsReceived Он у меня срабатывает в момент завершения боя ещё на карте этого боя (до ангара). И срабатывает только так, если выйти в ангар и ждать результаты боя не вызывается, если войти в другой бой тоже. Хотя был мод, который отображал результаты прошлого боя в новом бою, а сейчас вообще официально в клиенте есть, значит эта информация с сервера отправляется, но куда я не нашел @ Quote Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted August 25, 2021 Share Posted August 25, 2021 6 минут назад, SoprachevAK сказал: Хотя был мод, который отображал результаты прошлого боя в новом бою, а сейчас вообще официально в клиенте есть, значит эта информация с сервера отправляется, но куда я не нашел Результаты боев приходят в кэш и надо его оттуда дергать. Кэш, это файлы вот тут .../AppData/Roaming/Wargaming.net/WorldOfTanks\battle_results\..... А дергать если правильно помню в модах делают это через BigWorld.player().battleResultsCache. Ниже очередной чей-то древний код из моей библиотеки, что насобирал, он думаю тебе поможет. Это как дергать через battleResultsCache # -*- coding: utf-8 -*- import BigWorld, threading from Queue import Queue from functools import partial from PlayerEvents import g_playerEvents from messenger.formatters.service_channel import BattleResultsFormatter Threads = True ArenaIDQueue = Queue() ResultsQueue = Queue() old_format = BattleResultsFormatter.format def init(): g_playerEvents.onAvatarReady += IntoBattle g_playerEvents.onAccountBecomePlayer += self.onAccountBecomePlayer def onAccountBecomePlayer(): Results.ResultsAvailable.set() def IntoBattle(): Results.ResultsAvailable.clear() def format(self, message, *args): arenaUniqueID = message.data.get('arenaUniqueID', 0) ArenaIDQueue.put(arenaUniqueID) return old_format(self, message, *args) def fini(): Threads = False class BattleResultParser(object): def __init__(self): self.ResultsCache = [] self.ResultsAvailable = threading.Event() self.thread = threading.Thread(target = self.WaitResult) self.thread.setDaemon(True) self.thread.setName('WaitResult') self.thread.start() def CheckCallback(self, ArenaUniqueID, ErrorCode, Result): if ErrorCode in [-3, -5]: BigWorld.callback(1.0, lambda: ArenaIDQueue.put(ArenaUniqueID)) elif ErrorCode >= 0: if ArenaUniqueID in self.ResultsCache: return print Result.get('arenaUniqueID') print Result.get('personal') print Result.get('common') def WaitResult(self): while Threads: ArenaUniqueID = ArenaIDQueue.get() self.ResultsAvailable.wait() try: BigWorld.player().battleResultsCache.get(ArenaUniqueID, partial(self.CheckCallback, ArenaUniqueID)) except: pass BattleResultsFormatter.format = format Results = BattleResultParser() А тут вручную этот файл перехватывают и дампят, зачем, я хз import BigWorld import Math from gui.shared.utils.requesters import StatsRequester import AccountCommands import cPickle import zlib import os import base64 from battle_results_shared import * from messenger.proto.bw import ServiceChannelManager from functools import partial from gui import ClientHangarSpace from debug_utils import * BATTLE_RESULTS_VERSION = 1 CACHE_DIR = os.path.join(os.path.dirname(unicode(BigWorld.wg_getPreferencesFilePath(), 'utf-8', errors='ignore')), 'battle_results') todolist = [] waiting = False def new_valueResponse(self, responseCode, value = None, revision = 0): if responseCode < 0: LOG_NOTE('Battleresult not available or already retrieved. Response Code=%s' % responseCode) elif StatsRequester.__callback: StatsRequester.__callback(value) def fetchresult(arenaUID): global waiting if arenaUID: waiting = True proxy = partial(__onGetResponse, new_valueResponse) BigWorld.player()._doCmdInt3(AccountCommands.CMD_REQ_BATTLE_RESULTS, arenaUID, 0, 0, proxy) def __onGetResponse(callback, requestID, resultID, errorStr, ext = {}): global waiting if resultID != AccountCommands.RES_STREAM: waiting = False if callback is not None: try: callback(resultID, None) except: LOG_CURRENT_EXCEPTION() else: BigWorld.player()._subscribeForStream(requestID, partial(__onStreamComplete, callback)) def __onStreamComplete(callback, isSuccess, data): try: battleResults = cPickle.loads(zlib.decompress(data)) save(BigWorld.player().name, battleResults) except: LOG_CURRENT_EXCEPTION() if callback is not None: callback(AccountCommands.RES_FAILURE, None) def getFolderName(accountName, arenaUniqueID): battleStartTime = arenaUniqueID & 4294967295L battleStartDay = battleStartTime / 86400 return os.path.join(CACHE_DIR, base64.b32encode('%s;%s' % (accountName, battleStartDay))) def save(accountName, battleResults): fileHandler = None try: arenaUniqueID = battleResults[0] LOG_NOTE('Saving results of arenaUniqueID:', arenaUniqueID) folderName = getFolderName(accountName, arenaUniqueID) LOG_NOTE('Savefile Folder:', folderName) if not os.path.isdir(folderName): os.makedirs(folderName) fileName = os.path.join(folderName, '%s.dat' % arenaUniqueID) fileHandler = open(fileName, 'wb') cPickle.dump((BATTLE_RESULTS_VERSION, battleResults), fileHandler, -1) except: LOG_CURRENT_EXCEPTION() if fileHandler is not None: fileHandler.close() old_msg = ServiceChannelManager._ServiceChannelManager__addServerMessage def new_msg(self, message): global todolist LOG_NOTE('Message:', message) if message.type == 2: try: todolist.append(message.data.get('arenaUniqueID', 0)) except: LOG_CURRENT_EXCEPTION() old_msg(self, message) ServiceChannelManager._ServiceChannelManager__addServerMessage = new_msg old_setup = ClientHangarSpace._VehicleAppearance._VehicleAppearance__doFinalSetup def new_setup(self, buildIdx, model, delModel): global todolist if todolist: LOG_NOTE('Start work on my ToDoList:', todolist) while todolist: if waiting: time.delay(0.5) else: temp = todolist.pop() fetchresult(int(temp)) old_setup(self, buildIdx, model, delModel) ClientHangarSpace._VehicleAppearance._VehicleAppearance__doFinalSetup = new_setup @ Quote Link to comment Short link Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.