Jump to content
Korean Random
SoprachevAK

Сбор статистики о попаданиях

Recommended Posts

56 minutes ago, yepev said:

Я понимаю за что ты переживаешь, и именно привязанность данных к человеку как раз решает твою проблему. Ведь ему придётся завести огромное количество аккаунтов и айпишников, чтобы статистически значимо заспамить сервер в таком раскладе. Если привязать данные к аккаунтам через токены и сделать грамотные лимиты по приёму данных с каждого токена (на основе IP, rate limits итд), то проблема будет решена на корню, а не полумерами с детским псевдошифрованием.

Делать привязку к аккаунту я точно не буду, потому что это крайне некомфортно для пользователей, а вот ratelimit думаю есть смысл, хотя бы на бои, потому что выстрелы могут быть с пулемётного танка по 400 за бой

Ну и если делать ограничение по ip, нет никакого смысла делать привязку к аккаунту

И всё таки, даже детская защита, способная отбить 95% хакеров (детей), лучше чем её отсутствие. Если оставлять только защиту по ip, то будет много желающих 400 раз за 8 минут поспамить. 
 

И получается, что делать защиту от детей надо, а делать защиту от хакеров бессмысленно, её всё равно найдут способ обойти, а я не банковский сервис делаю, и деньги не потеряю, так на кой тогда черт париться и усложнять пользователям жизнь

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
3 часа назад, SoprachevAK сказал:

Что если строить обратную траекторию, как параболу через опорную точку и место попадания, зная скорость снаряда и гравитацию это можно сделать. Так можно избавиться от векторов присланных сервером. 

 

Да, это вариант. Зная shotPos, velocityVect, gravityVect и HitPoint, по ним вычисляем аналитически реальный вектор снаряда в точке shotPos, после чего сравниваем его с исходным velocityVect. И всё. Больше ничего не требуется. Кода минимум.

 

3 часа назад, SoprachevAK сказал:

Клиент отправляет только исходные данные

 

Ну тогда это всем другое дело. Клиентская часть не важна, она получается обычный и ничего не содержит важного. А код функции расчета дисперсии с сервера с пояснениями по расчетной модели можно и в теме запостить.

 

2 часа назад, SoprachevAK сказал:

пулемётного танка

 

Я бы исключил пулеметные танки. По крайней мере на первых порах. Это песок там всегда мало кого интересовало что и куда там летит.

 

3 часа назад, SoprachevAK сказал:

Ну насколько я понял, он как раз показал, что сервер НЕ оперирует распределение Релея, в отличие от новости на портале. Тк распределение Релея = 0 в центре круга, а в танках оно нулю не равно, что видно из его же графика ниже.

 

Ну да, там модифицированная версия распределения Релея с набросом точек в центре, иначе пригорать будет. А в последние два года тем еще правили это место искусственно. Новость на портале ВГ выходило об этих правках. 

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites
8 hours ago, StranikS_Scan said:

Ну тогда это всем другое дело. Клиентская часть не важна, она получается обычный и ничего не содержит важного. А код функции расчета дисперсии с сервера с пояснениями по расчетной модели можно и в теме запостить.

Думаю так и сделаю, когда допишу всё и появится время

 

8 hours ago, StranikS_Scan said:

Я бы исключил пулеметные танки.

В этом преимущество проекта, на сайте ты можешь посмотреть график по любой выборке, в том числе и исключая низкие уровни и САУ, как например настроено здесь https://wotstat.soprachev.com/analytics/61065e454c2ef064639d3004
Ну а лично мне тут интересно управление высокими нагрузками, для этого конечно надо бд заполнить побольше 

Share this post


Link to post

Short link
Share on other sites

Создал тему с планами на 2 версию мода, идеи предложения туда
Ну а сюда тогда вопрос, можно ли хоть сколько нибудь достоверно получать информацию о толщине брони в точке по вектору, был раньше модик который показывал приведённю броню  и шанс пробития, можно ли такое сейчас считать и можно ли например считать для своего танка, мол какая была вероятность что меня пробили бы
Я видел мод для расчёта реальной точки попадания, но насколько я понял это уже после боя, а мне хотелось бы во время игры и чтоб не лагало

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
6 часов назад, SoprachevAK сказал:

Я видел мод для расчёта реальной точки попадания, но насколько я понял это уже после боя, а мне хотелось бы во время игры и чтоб не лагало

 

Нет, это в самой игре. А будет ли это реплей или реальный бой - не имеет значения. Лагать не будет, это очень слабая нагрузка.

Share this post


Link to post

Short link
Share on other sites

@SoprachevAK а зачем две темы? Эта одна и для мода ещё одна. Можно было бы мод в этой же теме добавить в шапку. Было б удобнее, сразу можно прочитать обсуждение и понять что к чему и откуда пошло )

 

Да и название "Сбор и Анализ игровой статистики" вообще не привлекательное. Если б я заранее не знал, что за мод имеется ввиду, точно не кликнул бы на такое. Подумал бы, что очередной клон какого-нибудь мода про стату, а ведь на самом деле тут вообще по другому)

 

Вот если б назвался "Мод для анализа подкруток", то это было б кликбейтно (в хорошем смысле).


Спасибо, что открыл код. Крутой мув. Покопаюсь, когда будет время)

Share this post


Link to post

Short link
Share on other sites
14 hours ago, yepev said:

@SoprachevAK а зачем две темы? Эта одна и для мода ещё одна. Можно было бы мод в этой же теме добавить в шапку. Было б удобнее, сразу можно прочитать обсуждение и понять что к чему и откуда пошло )

 

Да и название "Сбор и Анализ игровой статистики" вообще не привлекательное. Если б я заранее не знал, что за мод имеется ввиду, точно не кликнул бы на такое. Подумал бы, что очередной клон какого-нибудь мода про стату, а ведь на самом деле тут вообще по другому)

 

Вот если б назвался "Мод для анализа подкруток", то это было б кликбейтно (в хорошем смысле).


Спасибо, что открыл код. Крутой мув. Покопаюсь, когда будет время)

Новая тема идеологически вообще о другом моде
Тут обсуждались тупые вопросы мол "как сделать" анализ полёта снаряда, там общий сбор различных событий, где траектория снаряда лишь частный случай. Менять общую повестку на 3 странице я не сторонник, как по мне, лучше много маленьких тем, чем одна большая 
Уже накидал примерную основу нового мода, думаю на этой неделе начну делать бэк, а дальше добавление новых событий будет лишь вопросом питона в моде

Название темы поменяю на кликбейт)

Share this post


Link to post

Short link
Share on other sites
On 8/1/2021 at 2:30 PM, StranikS_Scan said:

HitPoint

HipPoint правильно брать от сюда (мне кажется сомнительным тк тоже связано с трассером, но другого я не нашел)?
 

И ещё назрел вопрос о результатах боя, насколько я нагуглил у картошки нет способа через API их получить и если человек закрыл игру не дождавшись результата, то я его никак не узнаю. А если не закрыл, то результат брать от сюда? мне надо в том числе и во время другого боя, и по завершению текущего, в игре ещё не тыкал, сейчас совсем времени нет в танки играть. 

Share this post


Link to post

Short link
Share on other sites
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)

 

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
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 by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
3 часа назад, SoprachevAK сказал:

с выстрелом в скайбокс или на расстояние дальше дистанции стрельбы

 

Их нужно игнорировать. Они бессмысленны. 

 

3 часа назад, SoprachevAK сказал:

Возможно такая ситуация будет ещё с выстрелом по незамеченному противнику, там вроде бы снаряд просто пропадает, но ещё не тестил

 

Снаряд пропадает. Надо тоже игнорировать.

 

3 часа назад, SoprachevAK сказал:

Например попадание по танку может отправляться более точно, чем по рельефу, и получится что точность попаданий будет отличаться от точности промахов, а это нехилый камень в огород отсутствия подкрутки

 

Ну должно быть три типа выстрела: 

- в небо, перелёт, пропал, пулемётные танки: эти игнорируем

- попадание в ландшафт: грузим на сервер с пометкой вида

- попадание в коллижен: грузим на сервер с пометкой вида

 

Share this post


Link to post

Short link
Share on other sites
9 hours ago, StranikS_Scan said:

Их нужно игнорировать. Они бессмысленны. 

 

Они являются выстрелами, траекторию которых определил рандом. Если не учитывать часть выстрелов, в итоговом распределение получится ошибка выжившего. 

Понятное дело, что таких выстрелов мало, и на распределение они повлияют не сильно, но например на алгоритмы анализа случайных последовательностей, повлиять могут.

Буду собирать и трассёры и точки попаданий если такие есть. 

Share this post


Link to post

Short link
Share on other sites

А разве игнор перелётов не даст смещение средней точки попадания вниз?

Как раз получится доказательство, что "убить землю" больше шансов

Share this post


Link to post

Short link
Share on other sites
1 час назад, SoprachevAK сказал:

Они являются выстрелами, траекторию которых определил рандом. Если не учитывать часть выстрелов, в итоговом распределение получится ошибка выжившего. 

Понятное дело, что таких выстрелов мало, и на распределение они повлияют не сильно, но например на алгоритмы анализа случайных последовательностей, повлиять могут.

Буду собирать и трассёры и точки попаданий если такие есть. 

 

И как результат забьешь БД мусором, а потом получишь интегральные показатели, которые ничего не показывают. Качество анализа напрямую зависит от качества исходных данных. 

8 минут назад, KPOT2338 сказал:

А разве игнор перелётов не даст смещение средней точки попадания вниз?

Как раз получится доказательство, что "убить землю" больше шансов

 

Тут не все так однозначно. Ведь качество собранного материала, а точнее точность с которой определен разброс выстрела модом очень разнится в зависимости от ситуации. 

Share this post


Link to post

Short link
Share on other sites
45 minutes ago, StranikS_Scan said:

И как результат забьешь БД мусором

Могу позволить, новая версия использует колоночную СУБД (увеличение количества столбцов не влияет на производительность) 

47 minutes ago, StranikS_Scan said:

интегральные показатели, которые ничего не показывают

Спорно. Есть методы нуждающиеся в полной исходной последовательности. 
Тае же нашел свой университетский учебник, где 70 страниц посвящено анализу псевдослучайных последовательностей. 
Со 118 по 200 страницы, лично я в универе по нему делал тесты Кнута, крайне неприятное занятие, но может кому будет интересно глянуть

Иванов М.А., Чугунков И.В._Теория, применение и оценка качества генераторов псевдослучайных последовательностей.pdf

Share this post


Link to post

Short link
Share on other sites
10 часов назад, SoprachevAK сказал:

Спорно. Есть методы нуждающиеся в полной исходной последовательности. 

 

Дык о какой полноте может идти речь, если выборка изначально неполна и субъективна? Ты же собираешь не первичные данные, типа чисел на выходе ГПСЧ, а координаты попадания снарядов. У тебя изначально нет и не будет никакой "полной исходной последовательности", только её отголоски.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
8 hours ago, StranikS_Scan said:

 

Дык о какой полноте может идти речь, если выборка изначально неполна и субъективна? Ты же собираешь не первичные данные, типа чисел на выходе ГПСЧ, а координаты попадания снарядов. У тебя изначально нет и не будет никакой "полной исходной последовательности", только её отголоски.

Эти методы дают на выходе некое число, характеризующие качество случайности, ну и ожидается что это качество будет как минимум одинаковым у всех. 
Игроки ожидают случайное поведение рандома лично для себя, и именно его и стоит проверять.

Share this post


Link to post

Short link
Share on other sites
On 8/20/2021 at 6:06 AM, StranikS_Scan said:

мод смог перехватить onBattleResultsReceived

Он у меня срабатывает в момент завершения боя ещё на карте этого боя (до ангара). И срабатывает только так, если выйти в ангар и ждать результаты боя не вызывается, если войти в другой бой тоже. 

Хотя был мод, который отображал результаты прошлого боя в новом бою, а сейчас вообще официально в клиенте есть, значит эта информация с сервера отправляется, но куда я не нашел 

Share this post


Link to post

Short link
Share on other sites
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

 

Share this post


Link to post

Short link
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...