Jump to content
Korean Random
kharlashkin

[Заказ] Обновление мода Xinput-вибраций для геймпада

Recommended Posts

Доброе время суток всему уважаемому korean-random сообществу!

 

С последним обновлением 0.9.10 не работает вибрация.

post-19155-0-18729200-1442425535_thumb.png

 

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

 

Любую информацию интересующимся предоставлю :)

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

Вспомнил напутствие @sirmax попробовал запускать посторонние процессы (GPService.pyc) через subprocess - ничего не получилось. Может картоха теперь блокирует запуск посторонних процессов из игры?


P.S. Увидел на dev.modxvm.com что ребята используют свои библиотеки в XVM, мне не показалось? Возможно ли тогда ctypes прикрутить?


Поспешил я немного с выводом относительно запуска - всё работает, но не вибрация.

os.spawnl(os.P_NOWAIT, self.__sysPythonPath + ' ' + fileNameGPService)

Подскажите кто нибудь, что где менять чтобы загружался мод с помощью XFW?

init.py

XFW_MOD_INFO = {
    'VERSION':       '0.6.0',
    'URL':           'http://www.koreanrandom.com/forum/user/16753-inj3ct0r/',
    'UPDATE_URL':    'http://www.koreanrandom.com/forum/user/19155-kharlashkin/',
    'GAME_VERSIONS': ['0.9.10']
}

from xfw import *
from GamePadVibration import GamePadVibration
from Vibroeffects.VibroManager import VibroManager

def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)

RegisterEvent(VibroManager, '__init__', myVibroManager__init__)

 

GamePadVibration.py

import urllib2

class GamePadVibration:

    def __init__(self, originalVibrationObject):
        self.__originalVibrationObject = originalVibrationObject
        self.__gpServiceAddress = 'http://127.0.0.1:5000/'
        self.__gpServiceAddressTimeOut = 0.25
        self.__sysPythonPath = 'c:/python27/pythonw.exe'
        import os 
        fileNameGPService = os.path.dirname(os.path.realpath(__file__)) + '/GPService.pyc'
        os.spawnl(os.P_NOWAIT, self.__sysPythonPath + ' ' + fileNameGPService)

    def __del__(self):
        address = self.__gpServiceAddress
        try:
            urllib2.urlopen(address + 'disconnect')
        except urllib2.URLError as e:
            print 'urllib2.URLError (disconnect):', e
        except urllib2.HTTPError as e:
            print 'urllib2.HTTPError (disconnect):', e
        except:
            print 'urllib2: Unexpected error (disconnect)'

    def reset(self):
        self.__originalVibrationObject.reset()

    def setGain(self, gain):
        self.__originalVibrationObject.setGain(gain)

    def createEffect(self):
        effectHandle = self.__originalVibrationObject.createEffect()
        return effectHandle

    def getEffectLength(self, handle):
        durationInMs = self.__originalVibrationObject.getEffectLength(handle)
        return durationInMs

    def deleteEffect(self, handle):
        self.__originalVibrationObject.deleteEffect(handle)

    def connect(self):
        address = self.__gpServiceAddress
        isServiceRunning = False
        try:
            isServiceRunning = urllib2.urlopen(address + 'connect').read()
        except urllib2.URLError as e:
            print 'urllib2.URLError (connect):', e
        except urllib2.HTTPError as e:
            print 'urllib2.HTTPError (connect):', e
        except:
            print 'urllib2: Unexpected error (connect)'

        if isServiceRunning == 'True':
            return True
        else:
            return False

    def loadEffectFromFile(self, effectHandle, fileName):
        self.__originalVibrationObject.loadEffectFromFile(effectHandle, fileName)
        address = self.__gpServiceAddress
        timeOut = self.__gpServiceAddressTimeOut
        urlSafeOpen(address, '?effectHandle=' + str(effectHandle) + '&fileName=' + str(fileName), 'loadEffectFromFile', timeOut)

    def startEffect(self, handle, effectsSettingsCount):
        address = self.__gpServiceAddress
        timeOut = self.__gpServiceAddressTimeOut
        urlSafeOpen(address, '?handle=' + str(handle) + '&count=' + str(effectsSettingsCount), 'startEffect', timeOut)

    def stopEffect(self, handle):
        address = self.__gpServiceAddress
        timeOut = self.__gpServiceAddressTimeOut
        urlSafeOpen(address, '?handle=' + str(handle), 'stopEffect', timeOut)

    def setEffectGain(self, vibroEffectHandle, effectGain):
        address = self.__gpServiceAddress
        timeOut = self.__gpServiceAddressTimeOut
        urlSafeOpen(address, '?vibroEffectHandle=' + str(vibroEffectHandle) + '&effectGain=' + str(effectGain), 'setEffectGain', timeOut)

    def cloneEffect(self, sourceEffectHandle):
        destEffectHandle = self.__originalVibrationObject.cloneEffect(sourceEffectHandle)
        address = self.__gpServiceAddress
        timeOut = self.__gpServiceAddressTimeOut
        urlSafeOpen(address, '?sourceEffectHandle=' + str(sourceEffectHandle) + '&destEffectHandle=' + str(destEffectHandle), 'cloneEffect', timeOut)
        return destEffectHandle

def urlSafeOpen(addr, req, funcName, timeOut = 0.25):
    try:
        urllib2.urlopen(addr + funcName + req, None, timeOut)
    except urllib2.URLError as e:
        print funcName, 'urllib2.URLError:', e
    except urllib2.HTTPError as e:
        print funcName, 'urllib2.HTTPError:', e
    except:
        print funcName, 'urllib2: Unexpected error'
    return

import datetime

def _log_ms(msg):
    dt = datetime.datetime.now()
    print datetime.datetime.now().strftime('%H:%M:%S.') + str(dt.microsecond)[0:3] + ':', msg 

 

GPService.py

TIME_DELTA = 10
K_SIT_LEFT = [0.7, 0.0]
K_SIT_RIGHT = [0.7, 0.0]
K_BACK_LOW_LEFT = [0.3, 0.3]
K_BACK_LOW_RIGHT = [0.3, 0.3]
K_BACK_MID_LEFT = [0.0, 0.7]
K_BACK_MID_RIGHT = [0.0, 0.7]
LOG_CLASS_ON = False
LOG_ROUTE_ON = False
LOG_SERV_ON = False
transferCoefficientDict = {1: K_SIT_RIGHT,
 2: K_BACK_LOW_RIGHT,
 3: K_BACK_MID_RIGHT,
 9: K_SIT_LEFT,
 10: K_BACK_LOW_LEFT,
 11: K_BACK_MID_LEFT}

def _testBit(zone_group, zone):
    mask = 1 << zone
    return bool((zone_group & mask) >> zone)


import threading, time
from gpXInput import gpXInputClass

class gpVibroEffect:
    isRunning = property(lambda self: self.__isRunning)
    stopEvent = property(lambda self: self.__stopEvent)
    fileName = property(lambda self: self.__fileName)
    effectLength = property(lambda self: self.__effectLength)

    def __init__(self):
        self.__isRunning = False
        self.__isLoaded = False
        self.__effectLength = 0
        self.__vibrationsArrayLeftMotor = []
        self.__vibrationsArrayRightMotor = []
        self.__effectGain = 1.0
        self.__stopEvent = threading.Event()
        self.__stopEvent.clear()

    def setXInputObject(self, gpXInputObject):
        self.__gpXInputObject = gpXInputObject

    def play(self, count = 1):
        startTime = time.clock()
        if LOG_CLASS_ON:
            print self.__fileName, 'is started'
        lValue = 0.0
        rValue = 0.0
        gain = self.__effectGain
        import sys
        if count > sys.maxint:
            count = sys.maxint
        for c in xrange(count):
            if self.__stopEvent.isSet():
                break
            for x in range(int(self.__effectLength / TIME_DELTA)):
                if x == 0 and c == 0:
                    lDelta = self.__vibrationsArrayLeftMotor[x]
                    rDelta = self.__vibrationsArrayRightMotor[x]
                else:
                    lDelta = self.__vibrationsArrayLeftMotor[x] - self.__vibrationsArrayLeftMotor[x - 1]
                    rDelta = self.__vibrationsArrayRightMotor[x] - self.__vibrationsArrayRightMotor[x - 1]
                lValue += lDelta
                rValue += rDelta
                self.__gpXInputObject.adjust_vibration(lDelta * gain, rDelta * gain)
                if LOG_CLASS_ON:
                    print x, '%0.2f, %0.2f' % (lValue, rValue)
                if self.__stopEvent.isSet():
                    break
                time.sleep(2 * TIME_DELTA / 1000.0)

        lDelta = -lValue
        lValue += lDelta
        rDelta = -rValue
        rValue += rDelta
        self.__gpXInputObject.adjust_vibration(lDelta * gain, rDelta * gain)
        finishTime = time.clock()
        self.__isRunning = False
        if LOG_CLASS_ON:
            print x + 1, '%0.2f, %0.2f' % (lValue, rValue)
            print 'Time elapsed: %.3fs' % (finishTime - startTime)
            print self.__fileName, 'is stopped'

    def loadEffectFromFile(self, fileName):
        self.__isLoaded = False
        self.__fileName = fileName
        if LOG_CLASS_ON:
            print 'loading', self.__fileName
        from xml.etree import ElementTree as ET
        uwvTree = ET.parse(self.__fileName)
        uwvRoot = uwvTree.getroot()
        effectLength = 0
        for node in uwvRoot.getiterator():
            if 'time' in node.attrib:
                if int(node.attrib['time']) > effectLength:
                    effectLength = int(node.attrib['time'])

        self.__effectLength = effectLength
        for i in range(int(self.__effectLength / TIME_DELTA)):
            self.__vibrationsArrayLeftMotor.append(0)
            self.__vibrationsArrayRightMotor.append(0)

        for vibration in uwvRoot.getchildren():
            if 'vibration' in vibration.tag:
                timePrev = 0
                amplPrev = 0
                zones = int(vibration.attrib['zones'], 16)
                zoneLength = 0
                for vpoint in vibration.getchildren():
                    if int(vpoint.attrib['time']) > zoneLength:
                        zoneLength = int(vpoint.attrib['time'])

                self.__zonesGroupArrayLeftMotor = []
                self.__zonesGroupArrayRightMotor = []
                for i in range(int(zoneLength / TIME_DELTA) + 1):
                    self.__zonesGroupArrayLeftMotor.append(0)
                    self.__zonesGroupArrayRightMotor.append(0)

                for vpoint in vibration.getchildren():
                    timeCur = int(vpoint.attrib['time'])
                    amplCur = float(vpoint.attrib['amplitude'])
                    if timePrev == timeCur and amplPrev != amplCur:
                        x = int(timeCur / TIME_DELTA)
                        ampl = amplCur
                        for key in transferCoefficientDict.keys():
                            if _testBit(zones, key):
                                pass

                    elif timePrev != timeCur and amplPrev == amplCur:
                        for x in range(int(timePrev / TIME_DELTA), int(timeCur / TIME_DELTA)):
                            ampl = amplCur
                            for key in transferCoefficientDict.keys():
                                if _testBit(zones, key):
                                    self.__zonesGroupArrayLeftMotor[x] += ampl * transferCoefficientDict[key][0]
                                    self.__zonesGroupArrayRightMotor[x] += ampl * transferCoefficientDict[key][1]

                    elif '1' in vpoint.tag and timeCur != 0:
                        x = int(timeCur / TIME_DELTA)
                        ampl = amplCur
                        for key in transferCoefficientDict.keys():
                            if _testBit(zones, key):
                                pass

                    elif timePrev != timeCur and amplPrev != amplCur:
                        x1 = int(timePrev / TIME_DELTA)
                        x2 = int(timeCur / TIME_DELTA) - 1
                        y1 = amplPrev
                        y2 = amplCur
                        try:
                            k = (y2 - y1) / (x2 - x1)
                            b = y2 - k * x2
                        except:
                            k = 0
                            b = 0

                        for x in range(int(timePrev / TIME_DELTA), int(timeCur / TIME_DELTA)):
                            ampl = k * x + b
                            for key in transferCoefficientDict.keys():
                                if _testBit(zones, key):
                                    self.__zonesGroupArrayLeftMotor[x] += ampl * transferCoefficientDict[key][0]
                                    self.__zonesGroupArrayRightMotor[x] += ampl * transferCoefficientDict[key][1]

                    timePrev = timeCur
                    amplPrev = amplCur

                looped = vibration.attrib['looped']
                if looped == 'true':
                    y = 0
                    for x in range(int(effectLength / TIME_DELTA)):
                        self.__vibrationsArrayLeftMotor[x] += self.__zonesGroupArrayLeftMotor[y]
                        self.__vibrationsArrayRightMotor[x] += self.__zonesGroupArrayRightMotor[y]
                        y += 1
                        if y == int(zoneLength / TIME_DELTA):
                            y = 0

                else:
                    for x in range(int(zoneLength / TIME_DELTA)):
                        self.__vibrationsArrayLeftMotor[x] += self.__zonesGroupArrayLeftMotor[x]
                        self.__vibrationsArrayRightMotor[x] += self.__zonesGroupArrayRightMotor[x]

        self.__isLoaded = True

    def startEffect(self, count = 1):
        if not self.__isLoaded:
            return
        if self.__isRunning:
            self.__stopEvent.set()
            while self.__isRunning:
                pass

        self.__stopEvent.clear()
        self.__thread = threading.Thread(target=self.play, args=(count,))
        self.__isRunning = True
        self.__thread.start()

    def stopEffect(self):
        self.__stopEvent.set()

    def setEffectGain(self, effectGain):
        self.__effectGain = effectGain / 100.0


effectsDict = dict()
gpXInputObject = gpXInputClass()
gpXInputObject.stop_vibration()
from flask import Flask, request
if not LOG_SERV_ON:
    import logging
    log = logging.getLogger('werkzeug')
    log.setLevel(logging.ERROR)
app = Flask(__name__)

def shutdown_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    return


@app.route('/disconnect')
def disconnect():
    shutdown_server()
    for effect in effectsDict:
        effectsDict[effect].stopEffect()

    gpXInputObject.stop_vibration()
    time.sleep(2 * TIME_DELTA / 1000.0)
    return 'Server shutting down...'


@app.route('/connect')
def connect():
    if LOG_ROUTE_ON:
        print 'Connection checked - Ok.'
    return 'True'


@app.route('/loadEffectFromFile')
def loadEffectFromFile():
    effectHandle = request.args.get('effectHandle')
    fileName = request.args.get('fileName')
    import os
    fileName = os.path.dirname(__file__) + '/' + fileName
    if os.path.exists(fileName):
        effectsDict[effectHandle] = gpVibroEffect()
        effectsDict[effectHandle].setXInputObject(gpXInputObject)
        effectsDict[effectHandle].loadEffectFromFile(fileName)
    else:
        print 'loadEffectFromFile Error: file not found:', fileName
    if LOG_ROUTE_ON:
        print
        print 'loadEffectFromFile'
        print 'effectHandle =', effectHandle
        print 'fileName =', fileName
    return ''


@app.route('/startEffect')
def startEffect():
    handle = request.args.get('handle')
    count = int(request.args.get('count'))
    if handle in effectsDict:
        effectsDict[handle].startEffect(count)
    else:
        print 'startEffect Error: Handle', handle, 'is empty'
    if LOG_ROUTE_ON:
        print
        print 'startEffect'
        print 'handle =', handle
        print 'count =', str(count)
    return ''


@app.route('/stopEffect')
def stopEffect():
    handle = request.args.get('handle')
    if handle in effectsDict:
        effectsDict[handle].stopEffect()
    else:
        print 'stopEffect Error: Handle', handle, 'is empty'
    if LOG_ROUTE_ON:
        print
        print 'stopEffect'
        print 'handle =', handle
    return ''


@app.route('/setEffectGain')
def setEffectGain():
    vibroEffectHandle = request.args.get('vibroEffectHandle')
    effectGain = float(request.args.get('effectGain'))
    if vibroEffectHandle in effectsDict:
        effectsDict[vibroEffectHandle].setEffectGain(effectGain)
    else:
        print 'setEffectGain Error: Handle', vibroEffectHandle, 'is empty'
    if LOG_ROUTE_ON:
        print
        print 'setEffectGain'
        print 'vibroEffectHandle =', vibroEffectHandle
        print 'effectGain =', effectGain
    return ''


@app.route('/cloneEffect')
def cloneEffect():
    sourceEffectHandle = request.args.get('sourceEffectHandle')
    destEffectHandle = request.args.get('destEffectHandle')
    if sourceEffectHandle in effectsDict:
        effectsDict[destEffectHandle] = gpVibroEffect()
        effectsDict[destEffectHandle].setXInputObject(gpXInputObject)
        effectsDict[destEffectHandle].loadEffectFromFile(effectsDict[sourceEffectHandle].fileName)
    else:
        print 'cloneEffect Error: sourceHandle', sourceEffectHandle, 'is empty'
    if LOG_ROUTE_ON:
        print
        print 'cloneEffect'
        print 'sourceEffectHandle =', sourceEffectHandle
        print 'destEffectHandle =', destEffectHandle
    return ''


if __name__ == '__main__':
    app.run() 

 

gpXInput.py

import ctypes

class XINPUT_VIBRATION(ctypes.Structure):
    _fields_ = [('wLeftMotorSpeed', ctypes.c_ushort), ('wRightMotorSpeed', ctypes.c_ushort)]


class gpXInputClass:

    def __init__(self):
        xInputDLLFileName = 'xinput1_3'
        xinput = ctypes.WinDLL(xInputDLLFileName)
        self.__XInputSetState = xinput.XInputSetState
        self.__XInputSetState.argtypes = [ctypes.c_uint, ctypes.POINTER(XINPUT_VIBRATION)]
        self.__XInputSetState.restype = ctypes.c_uint
        self.__l = 0
        self.__r = 0

    def adjust_vibration(self, left_motor_delta, right_motor_delta, set = False, controller = 0):
        if set:
            self.__l = left_motor
            self.__r = right_motor
        else:
            self.__l += left_motor_delta
            self.__r += right_motor_delta
        tempL = self.__l
        tempR = self.__r
        if self.__l > 255:
            tempL = 255
        elif self.__l < 0:
            tempL = 0
        if self.__r > 255:
            tempR = 255
        elif self.__r < 0:
            tempR = 0
        vibration = XINPUT_VIBRATION(int(tempL * 65535 / 255), int(tempR * 65535 / 255))
        self.__XInputSetState(controller, ctypes.byref(vibration))

    def stop_vibration(self, controller = 0):
        vibration = XINPUT_VIBRATION(0, 0)
        self.__XInputSetState(controller, ctypes.byref(vibration))


gpXInputObject = gpXInputClass() 

Edited by kharlashkin

Share this post


Link to post

Short link
Share on other sites

Надо кое чего переделать под новый формат. Как-то так:

 

@registerEvent(VibroManager, '__init__')
def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

 

Надо кое чего переделать под новый формат. Как-то так:

@registerEvent(VibroManager, '__init__')
def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)

 

Спасибо! Попробую. Я в принципе уже докопался по исходникам XVM, что метод RegisterEvent устаревший (# deprecated) и нашел пример реализации @registerEvent в исходниках.

Share this post


Link to post

Short link
Share on other sites

Не сработало. Уважаемый @sirmax, а не могли бы Вы более полную инструкцию дать по методу @registerEvent? Читая Ваши исходники мне почему то кажется что мне необходимо каждый метод в классе VibroManager таким образом дополнить.

Share this post


Link to post

Short link
Share on other sites

Мне некогда сейчас этим заниматься.

Так я и не прошу Вас сделать что-то за меня (ну или за того кто захочет помочь) ;)

 

Просто судя по исходникам XVM с помощью @registerEvent в классах нужно расширять все методы по отдельности - собственно я и спрашиваю "Стоит ли пробовать?".

Share this post


Link to post

Short link
Share on other sites

Просто судя по исходникам XVM с помощью @registerEvent в классах нужно расширять все методы по отдельности - собственно я и спрашиваю "Стоит ли пробовать?".

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

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

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

Вот же, как раз под вторым спойлером и видно все методы которые необходимо расширить. А лекцию я бы послушал с удовольствием :)

Share this post


Link to post

Short link
Share on other sites

"Пришла беда откуда не ждали..."

 

Может кто встречался с подобным?

post-19155-0-76468800-1442772411_thumb.png

post-19155-0-25022000-1442772422_thumb.png

Share this post


Link to post

Short link
Share on other sites

Вот же, как раз под вторым спойлером и видно все методы которые необходимо расширить.

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

@registerEvent(..) это декоратор, по сути функция, которая получает аргументом функцию-хук. registerEvent - функция, которая возвращает декоратор. Есть статья по этой теме на хабре.

Что касаемо инструкции на registerEvent,

@registerEvent(VibroManager, '__init__')
def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)
в некотором приближении эквивалентно

def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)
    return wgVibroManager__init__(self)

wgVibroManager__init__ = VibroManager.__init__
VibroManager.__init__ = myVibroManager__init__
Опять же повторюсь - в приближении. На самом деле там алгоритм несколько сложнее, работает через event'ы.

 

Думаю, тебе стоит проверить вызываются ли методы твоего класса или нет. Принты допиши, посмотри что и как. А дальше уже будет видно, в какую сторону копать.

Надо кое чего переделать под новый формат. Как-то так:

Тоже тут написал одно время немного кода)

import functools
import types

class HookFunction(object):
	CALL_ORIGIN_BEFORE_HOOK = 0x0
	CALL_HOOK_BEFORE_ORIGIN = 0x1
	CALL_ORIGIN_INSIDE_HOOK = 0x2
	CALL_TYPE_DEFAULT = CALL_ORIGIN_BEFORE_HOOK
	
	def __init__(self, origin, hook, type = CALL_TYPE_DEFAULT, active = True):
		if not isinstance(hook, (types.FunctionType, types.LambdaType)):
			raise TypeError('Hook must be function or lambda')
		self.__name__ = hook.__name__
		self.origin = origin
		self.hook = hook
		self.type = type
		self.active = active
		return
	
	def __call__(self, *args, **kwargs):
		if self.active and self.type == self.CALL_ORIGIN_BEFORE_HOOK:
			result = self.origin(*args, **kwargs)
			self.hook(*args, **kwargs)
		elif self.active and self.type == self.CALL_HOOK_BEFORE_ORIGIN:
			self.hook(*args, **kwargs)
			result = self.origin(*args, **kwargs)
		elif self.active and self.type == self.CALL_ORIGIN_INSIDE_HOOK:
			result = self.hook(self.origin, *args, **kwargs)
		else:
			result = self.origin(*args, **kwargs)
		return result
	
	def __get__(self, instance, type = None):
		return types.MethodType(self, instance, type)
	
	@classmethod
	def makeMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method).__func__
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, override)
		else:
			setattr(target, method, override.__get__(target, types.TypeType(target)))
		return hook
	
	@classmethod
	def makeStaticMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method)
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, staticmethod(override))
		else:
			setattr(target, method, override)
		return hook
	
	@classmethod
	def makeClassMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method).__func__
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, classmethod(override))
		else:
			setattr(target, method, override.__get__(types.TypeType(target), types.TypeType))
		return hook

	@classmethod
	def methodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeMethodHook, target, method, type = type, active = active)
	
	@classmethod
	def staticMethodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeStaticMethodHook, target, method, type = type, active = active)
	
	@classmethod
	def classMethodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeClassMethodHook, target, method, type = type, active = active)
Последние три метода - декораторы.
Edited by GPCracker
  • Upvote 3

Share this post


Link to post

Short link
Share on other sites

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

@registerEvent(..) это декоратор, по сути функция, которая получает аргументом функцию-хук. registerEvent - функция, которая возвращает декоратор. Есть статья по этой теме на хабре.

Что касаемо инструкции на registerEvent,

@registerEvent(VibroManager, '__init__')
def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)

в некотором приближении эквивалентно

def myVibroManager__init__(self):
    self._VibroManager__vibrationObject = GamePadVibration(self._VibroManager__vibrationObject)
    return wgVibroManager__init__(self)

wgVibroManager__init__ = VibroManager.__init__
VibroManager.__init__ = myVibroManager__init__

Опять же повторюсь - в приближении. На самом деле там алгоритм несколько сложнее, работает через event'ы.

 

Думаю, тебе стоит проверить вызываются ли методы твоего класса или нет. Принты допиши, посмотри что и как. А дальше уже будет видно, в какую сторону копать.

Тоже тут написал одно время немного кода)

import functools
import types

class HookFunction(object):
	CALL_ORIGIN_BEFORE_HOOK = 0x0
	CALL_HOOK_BEFORE_ORIGIN = 0x1
	CALL_ORIGIN_INSIDE_HOOK = 0x2
	CALL_TYPE_DEFAULT = CALL_ORIGIN_BEFORE_HOOK
	
	def __init__(self, origin, hook, type = CALL_TYPE_DEFAULT, active = True):
		if not isinstance(hook, (types.FunctionType, types.LambdaType)):
			raise TypeError('Hook must be function or lambda')
		self.__name__ = hook.__name__
		self.origin = origin
		self.hook = hook
		self.type = type
		self.active = active
		return
	
	def __call__(self, *args, **kwargs):
		if self.active and self.type == self.CALL_ORIGIN_BEFORE_HOOK:
			result = self.origin(*args, **kwargs)
			self.hook(*args, **kwargs)
		elif self.active and self.type == self.CALL_HOOK_BEFORE_ORIGIN:
			self.hook(*args, **kwargs)
			result = self.origin(*args, **kwargs)
		elif self.active and self.type == self.CALL_ORIGIN_INSIDE_HOOK:
			result = self.hook(self.origin, *args, **kwargs)
		else:
			result = self.origin(*args, **kwargs)
		return result
	
	def __get__(self, instance, type = None):
		return types.MethodType(self, instance, type)
	
	@classmethod
	def makeMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method).__func__
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, override)
		else:
			setattr(target, method, override.__get__(target, types.TypeType(target)))
		return hook
	
	@classmethod
	def makeStaticMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method)
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, staticmethod(override))
		else:
			setattr(target, method, override)
		return hook
	
	@classmethod
	def makeClassMethodHook(sclass, target, method, hook, type = CALL_TYPE_DEFAULT, active = True):
		origin = getattr(target, method).__func__
		override = sclass(origin, hook, type, active)
		if isinstance(target, (types.TypeType, types.ClassType)):
			setattr(target, method, classmethod(override))
		else:
			setattr(target, method, override.__get__(types.TypeType(target), types.TypeType))
		return hook

	@classmethod
	def methodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeMethodHook, target, method, type = type, active = active)
	
	@classmethod
	def staticMethodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeStaticMethodHook, target, method, type = type, active = active)
	
	@classmethod
	def classMethodHook(sclass, target, method, type = CALL_TYPE_DEFAULT, active = True):
		return functools.partial(sclass.makeClassMethodHook, target, method, type = type, active = active)

Последние три метода - декораторы.

 

Жаль что плюсик один (допишу ещё жирных) - плюс, плюс, плюс!

Share this post


Link to post

Short link
Share on other sites

Декораторы - это хорошо конечно, но блин я никак не могу понять что изменилось то у картошек.

    def start(self):
        from gui.app_loader import g_appLoader
        g_appLoader.onGUISpaceChanged += self.__onGUISpaceChanged

    def __onGUISpaceChanged(self, spaceID):
        from gui.app_loader.settings import GUI_GLOBAL_SPACE_ID
        if spaceID == GUI_GLOBAL_SPACE_ID.LOGIN:
            self.stopAllEffects()

Share this post


Link to post

Short link
Share on other sites

 

Декораторы - это хорошо конечно, но блин я никак не могу понять что изменилось то у картошек.

    def start(self):
        from gui.app_loader import g_appLoader
        g_appLoader.onGUISpaceChanged += self.__onGUISpaceChanged

    def __onGUISpaceChanged(self, spaceID):
        from gui.app_loader.settings import GUI_GLOBAL_SPACE_ID
        if spaceID == GUI_GLOBAL_SPACE_ID.LOGIN:
            self.stopAllEffects()

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

Вообще, если ты ждешь какой-то толковый ответ, хотя бы логи покажи, и исходники. Кроме того, тебе уже говорили вставить в методы вывод в логи отладочной информации, чтобы можно было определить что вызывается, а что нет.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

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

Вообще, если ты ждешь какой-то толковый ответ, хотя бы логи покажи, и исходники. Кроме того, тебе уже говорили вставить в методы вывод в логи отладочной информации, чтобы можно было определить что вызывается, а что нет.

Вот же - я прихожу к тому же выводу, что мой сервис хоть и стартует но игрой не обрабатывается. Исходники мода сверху под спойлерами кроме серверной части с Flask и скриптом для вибрации с ctypes. Прикол в том что логи чистые :)

print добавлю.

python.log

xvm.log

Edited by kharlashkin

Share this post


Link to post

Short link
Share on other sites

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

пока ты логи не сделаешь, ничего не будет

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

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

пока ты логи не сделаешь, ничего не будет

 

Вместо плюсика :)

post-19155-0-25830300-1443198013_thumb.png

Share this post


Link to post

Short link
Share on other sites

 

 

Возможно ли тогда ctypes прикрутить?

что конкретно надо из ctypes? этот функционал можно сделать отдельным пидом под танковый питон.

Share this post


Link to post

Short link
Share on other sites

что конкретно надо из ctypes? этот функционал можно сделать отдельным пидом под танковый питон.

Собственно вот думаю будет понятно:

import ctypes

class XINPUT_VIBRATION(ctypes.Structure):
    _fields_ = [('wLeftMotorSpeed', ctypes.c_ushort), ('wRightMotorSpeed', ctypes.c_ushort)]


class gpXInputClass:

    def __init__(self):
        xInputDLLFileName = 'xinput1_3'
        xinput = ctypes.WinDLL(xInputDLLFileName)
        self.__XInputSetState = xinput.XInputSetState
        self.__XInputSetState.argtypes = [ctypes.c_uint, ctypes.POINTER(XINPUT_VIBRATION)]
        self.__XInputSetState.restype = ctypes.c_uint
        self.__l = 0
        self.__r = 0

    def adjust_vibration(self, left_motor_delta, right_motor_delta, set = False, controller = 0):
        if set:
            self.__l = left_motor
            self.__r = right_motor
        else:
            self.__l += left_motor_delta
            self.__r += right_motor_delta
        tempL = self.__l
        tempR = self.__r
        if self.__l > 255:
            tempL = 255
        elif self.__l < 0:
            tempL = 0
        if self.__r > 255:
            tempR = 255
        elif self.__r < 0:
            tempR = 0
        vibration = XINPUT_VIBRATION(int(tempL * 65535 / 255), int(tempR * 65535 / 255))
        self.__XInputSetState(controller, ctypes.byref(vibration))

    def stop_vibration(self, controller = 0):
        vibration = XINPUT_VIBRATION(0, 0)
        self.__XInputSetState(controller, ctypes.byref(vibration))


gpXInputObject = gpXInputClass()

Share this post


Link to post

Short link
Share on other sites

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

пока ты логи не сделаешь, ничего не будет

Добавил принты в код - ничего не получилось внятное у меня. Никогда логами не занимался, подскажите пример хороший пожалуйста. Edited by kharlashkin

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...