Добавлен N1ghtSiren,
опубликован
Раздел:
Триггеры и объекты
Хотел я значит найти способ использовать фреймы мемхака в мультиплеере, и таки нашёл.
Сперва нашёл относительно старую наработку, но она была слишком неудобной.
Потом полез в заведомо рабочую китайскую доту с магазинами на фреймах и нашёл вот чего:
Сперва нашёл относительно старую наработку, но она была слишком неудобной.
Потом полез в заведомо рабочую китайскую доту с магазинами на фреймах и нашёл вот чего:
function D0G takes nothing returns nothing
local timer t=GetExpiredTimer()
local unit s=ATB(t,115)
local integer i=ASB(t,105)
local real x=0
local real y=0
local real x1=GetStoredReal(AL,NKB(s),"MouseX") //загрузить из хэша
local real y1=GetStoredReal(AL,NKB(s),"MouseY")
local real a=0
if x1!=0 or y1!=0 then // если загрузилось
call ZSA() //функция чистки
set x=GetUnitX(s)
set y=GetUnitY(s)
set a=Atan2(y1-y,x1-x) //найти угол
call X3F(s,a,i) //начать шаманить
endif
set t=null
set s=null
endfunction
function EAG takes unit s,integer i returns nothing
local timer t=XXA() //взять таймер из кучи
local real x=GetMouseX() //взять мышь мемхаком
local real y=GetMouseY()
if GetLocalPlayer()==GetOwningPlayer(s)then //взять конкретного игрока
call StoreReal(AL,NKB(s),"MouseX",x) //засейвить мышь в хэш
call StoreReal(AL,NKB(s),"MouseY",y)
call SyncStoredReal(AL,NKB(s),"MouseX") //отправить кэш в синк
call SyncStoredReal(AL,NKB(s),"MouseY")
endif
call StoreReal(AL,NKB(s),"MouseX",0) //перезаписать коорды всем
call StoreReal(AL,NKB(s),"MouseY",0)
call AUB(t,115,s) //передача данных вниз по потоку или чёто такое
call ARB(t,105,i)
call TimerStart(t,.005,true,function D0G)
set t=null
endfunction
Как ни странно оно работало и десинков не вызывало.
Я написал свою версию этой фичи и выглядит она примерно вот так:
Я написал свою версию этой фичи и выглядит она примерно вот так:
раскрыть
globals
integer synced_int
integer synced_int2
real synced_time
//if you will need this number for syncing, change it
//this number indicates that "data-is-not-here-yet" and timer will wait for another number
integer no_data_marker = -8192
endglobals
struct Sync
static hashtable SyncHashTable = InitHashtable()
static gamecache SyncCache = InitGameCache("Sync")
static integer sync_offset = 8192
boolexpr onfinish
static method SyncInteger2Loop takes nothing returns nothing
local thistype this = LoadInteger(SyncHashTable, GetHandleId(GetExpiredTimer()), 0)
local integer loaded_int = GetStoredInteger(SyncCache, "0", I2S(this))
local integer loaded_int2 = GetStoredInteger(SyncCache, "0", I2S(this+sync_offset))
local boolean b = loaded_int != no_data_marker and loaded_int2 != no_data_marker
local trigger t
//call BJDebugMsg("SyncInteger2Loop: "+I2S(loaded_int)+", "+I2S(loaded_int2)+", boolexpr: "+I2S(GetHandleId(this.onfinish)))
if(b) then
set synced_int = loaded_int
set synced_int2 = loaded_int2
set synced_time = TimerGetElapsed(GetExpiredTimer())
//execute block of code
set t = CreateTrigger()
call TriggerAddCondition(t, this.onfinish)
call TriggerEvaluate(t)
call DestroyTrigger(t)
set t = null
//
call FlushStoredInteger(SyncCache, "0", I2S(this))
call FlushChildHashtable(SyncHashTable, GetHandleId(GetExpiredTimer()))
call DestroyTimer(GetExpiredTimer())
//you cant run multiple boolexpr of same function at same time
//because Condition(function DoNothing) will return same boolexp
//so save it outside and destroy later
//call DestroyBoolExpr(this.onfinish)
set this.onfinish = null
call this.destroy()
endif
endmethod
static method SyncMouseFrom takes player from_player, boolexpr onfinish returns nothing
local thistype this = thistype.create()
local timer t = CreateTimer()
set this.onfinish = onfinish
if(GetLocalPlayer()==from_player)then
call StoreInteger(SyncCache, "0", I2S(this), R2I(GetMouseWorldX()))
call StoreInteger(SyncCache, "0", I2S(this+sync_offset), R2I(GetMouseWorldY()))
call SyncStoredInteger(SyncCache, "0", I2S(this))
call SyncStoredInteger(SyncCache, "0", I2S(this+sync_offset))
endif
call StoreInteger(SyncCache, "0", I2S(this), no_data_marker)
call StoreInteger(SyncCache, "0", I2S(this+sync_offset), no_data_marker)
call SaveInteger(SyncHashTable, GetHandleId(t), 0, this)
call TimerStart(t, 0.03125, true, function Sync.SyncInteger2Loop)
endmethod
static method SyncInt2From takes player from_player, integer a, integer b, boolexpr onfinish returns nothing
local thistype this = thistype.create()
local timer t = CreateTimer()
set this.onfinish = onfinish
if(a == no_data_marker or b == no_data_marker)then
call BJDebugMsg("|cffff0000 Sync.SyncInt2From: trying to sync data that equals to no-data marker")
endif
if(GetLocalPlayer()==from_player)then
call StoreInteger(SyncCache, "0", I2S(this), a)
call StoreInteger(SyncCache, "0", I2S(this+sync_offset), b)
call SyncStoredInteger(SyncCache, "0", I2S(this))
call SyncStoredInteger(SyncCache, "0", I2S(this+sync_offset))
endif
call StoreInteger(SyncCache, "0", I2S(this), no_data_marker)
call StoreInteger(SyncCache, "0", I2S(this+sync_offset), no_data_marker)
call SaveInteger(SyncHashTable, GetHandleId(t), 0, this)
call TimerStart(t, 0.03125, true, function Sync.SyncInteger2Loop)
endmethod
static method onInit takes nothing returns nothing
set SyncCache = InitGameCache("Sync")
endmethod
endstruct
Тесты:
раскрыть
globals
unit mouse_move_unit
timer sync_timer
integer ints_to_sync = 700
integer synced_amount = 0
boolexpr sync_expr
endglobals
library SyncTest initializer Init_SyncTest
function MegaSyncOnFinish takes nothing returns nothing
call BJDebugMsg("Mega Sync finished")
call BJDebugMsg("synced "+I2S(ints_to_sync)+" 8-digit ints for "+R2S(TimerGetElapsed(sync_timer)))
call DestroyTimer(sync_timer)
call DestroyBoolExpr(sync_expr)
set sync_timer = null
endfunction
function MegaSyncOnSync takes nothing returns nothing
call BJDebugMsg("+"+R2S(synced_time)+"sec: received sync#"+I2S(synced_int2)+" with content "+I2S(synced_int))
set synced_amount = synced_amount + 1
if(synced_amount == ints_to_sync)then
call MegaSyncOnFinish()
endif
endfunction
function MegaSyncInit takes nothing returns nothing
local integer i
set sync_timer = CreateTimer()
set sync_expr = Condition(function MegaSyncOnSync)
call TimerStart(sync_timer, 9999, false, function DoNothing)
call EnableOPLimit(false)
set i = 0
loop
exitwhen i >= ints_to_sync
call Sync.SyncInt2From(Player(0), GetRandomInt(10000000, 99999999), i, sync_expr)
call BJDebugMsg(I2S(i))
set i = i + 1
endloop
endfunction
function MouseMoveTestLoop takes nothing returns nothing
local integer mousex = synced_int
local integer mousey = synced_int2
call SetUnitX(mouse_move_unit, mousex)
call SetUnitY(mouse_move_unit, mousey)
endfunction
function MouseMoveTestInit takes nothing returns nothing
call Sync.SyncMouseFrom(Player(0), Condition(function MouseMoveTestLoop))
endfunction
function Init_SyncTest takes nothing returns nothing
set mouse_move_unit = CreateUnit(Player(0), 'Hpal', 0., 0., 0.)
call TimerStart(CreateTimer(), 0.03125, true, function MouseMoveTestInit)
//call TimerStart(CreateTimer(), 1, false, function MegaSyncInit)
call PanCameraTo(0., 0.)
endfunction
endlibrary
Видосеки!
паладин перемещающийся по мыше красного игрока
синхронизация 700 интов
попытка синхронизации 2к интов
синхронизация 700 интов на пинге в 300
синхронизация паладина на пинге в 300
Теории о том как оно всё же работает и почему не больше 735
Работает оно потому что число из кэша сразу копируется в буфер синхронизации, и перезаписывая его мы ничего не теряем.
А функция синхронизации кэша гарантирует наличие этой информации у всех игроков (на это уходит несколько пакетов)
Мы же создаём периодический таймер который проверяет наличие информации в кэше и когда она появляется, закидывает информацию в глобалки и запускает булексп, а раз булексп запустился, значит глобалки должны быть на месте.
Среднее время синка - 0.16 секунд на ~500 8-значных интов, дальше оно какбы "дотекает" за следующие несколько секунд.
Почему лимит в 735 - возможно лимит ключей в кэше , что маловероятно, либо лимит одновременных таймеров что не запускают цикл проверки синка, либо внутренний лимит размера буфера синхронизации
Работает оно потому что число из кэша сразу копируется в буфер синхронизации, и перезаписывая его мы ничего не теряем.
А функция синхронизации кэша гарантирует наличие этой информации у всех игроков (на это уходит несколько пакетов)
Мы же создаём периодический таймер который проверяет наличие информации в кэше и когда она появляется, закидывает информацию в глобалки и запускает булексп, а раз булексп запустился, значит глобалки должны быть на месте.
Среднее время синка - 0.16 секунд на ~500 8-значных интов, дальше оно какбы "дотекает" за следующие несколько секунд.
Почему лимит в 735 - возможно лимит ключей в кэше , что маловероятно, либо лимит одновременных таймеров что не запускают цикл проверки синка, либо внутренний лимит размера буфера синхронизации
Моя либа не является "финальной" и лишь показывает как это можно использовать
Надеюсь посмотрев функции вы поймёте как добавить ещё пару глобалок и функций на нужные вам типы данных
Пример сделан с учётом функций мемхака (планирую использовать его), однако можно использовать и без него.
Дада, используем DoNothing() :3
Надеюсь посмотрев функции вы поймёте как добавить ещё пару глобалок и функций на нужные вам типы данных
Пример сделан с учётом функций мемхака (планирую использовать его), однако можно использовать и без него.
Дада, используем DoNothing() :3
Карта-пример
Upd: Обновлённая либа
Upd2: Ещё более обновлённая либа
теперь детектит открытый чат - требуется MemoryHackWenHaoAPI плагин
и активное окно варкрафта - просто вызывается из под винды
Upd2: Ещё более обновлённая либа
теперь детектит открытый чат - требуется MemoryHackWenHaoAPI плагин
и активное окно варкрафта - просто вызывается из под винды
раскрыть
library Sync
globals
integer pGetForegroundWindow = 0
endglobals
function IsWar3Active takes nothing returns boolean
if(pGetForegroundWindow == 0)then
set pGetForegroundWindow = GetModuleProcAddress( "User32.dll", "GetForegroundWindow" )
endif
return GetWarcraftWindow() == fast_call_1(pGetForegroundWindow, 0)
endfunction
//returns true if opened
function IsChatOpened takes nothing returns boolean
return WHGetChatState()
endfunction
globals
integer g_syncmx
integer g_syncmy
player g_syncplayer
integer g_syncpid
gamecache gcSync = InitGameCache("Sync")
hashtable htSync = InitHashtable()
integer intSync_data = 2
endglobals
struct SyncInstance
static integer offset = 8192
player from_player
timer keychecker
timer synchecker
integer keycode
boolexpr onfinish
//mousex + mousey
static method SyncMouseCheck takes nothing returns nothing
local thistype this = LoadInteger(htSync, GetHandleId(GetExpiredTimer()), 0)
local boolean synced = HaveStoredInteger(gcSync, "0", I2S(this)) and HaveStoredInteger(gcSync, "0", I2S(this+offset))
local trigger t
if(synced)then
set g_syncplayer = this.from_player
set g_syncpid = GetPlayerId(this.from_player)
set g_syncmx = GetStoredInteger(gcSync, "0", I2S(this))
set g_syncmy = GetStoredInteger(gcSync, "0", I2S(this+offset))
//
set t = CreateTrigger()
call TriggerAddCondition(t, this.onfinish)
call TriggerEvaluate(t)
call DestroyTrigger(t)
set t = null
call FlushStoredInteger(gcSync, "0", I2S(this))
call FlushStoredInteger(gcSync, "0", I2S(this+offset))
if(this.keychecker != null)then
call PauseTimer(this.keychecker)
call FlushChildHashtable(htSync, GetHandleId(this.keychecker))
call DestroyTimer(this.keychecker)
set this.keychecker = null
set this.keycode = 0
endif
call PauseTimer(this.synchecker)
call FlushChildHashtable(htSync, GetHandleId(this.synchecker))
call DestroyTimer(this.synchecker)
set this.onfinish = null
set this.synchecker = null
set this.from_player = null
call this.destroy()
endif
endmethod
//keypress flag
static method SyncKeyCheck takes nothing returns nothing
local thistype this = LoadInteger(htSync, GetHandleId(GetExpiredTimer()), 0)
local boolean synced = HaveStoredInteger(gcSync, "0", I2S(this))
local trigger t
if(synced)then
set g_syncplayer = this.from_player
set t = CreateTrigger()
call TriggerAddCondition(t, this.onfinish)
call TriggerEvaluate(t)
call DestroyTrigger(t)
set t = null
call PauseTimer(this.keychecker)
call PauseTimer(this.synchecker)
call FlushStoredInteger(gcSync, "0", I2S(this))
call FlushChildHashtable(htSync, GetHandleId(this.keychecker))
call FlushChildHashtable(htSync, GetHandleId(this.synchecker))
call DestroyTimer(this.keychecker)
call DestroyTimer(this.synchecker)
set this.onfinish = null
set this.keychecker = null
set this.synchecker = null
set this.from_player = null
set this.keycode = 0
call this.destroy()
endif
endmethod
static method LocalKeyCheck takes nothing returns nothing
local thistype this = LoadInteger(htSync, GetHandleId(GetExpiredTimer()), 0)
if(GetLocalPlayer() == this.from_player)then
if(IsChatOpened() == false and IsWar3Active())then
if(IsKeyPressed(this.keycode))then
call StoreInteger(gcSync, "0", I2S(this), intSync_data)
call SyncStoredInteger(gcSync, "0", I2S(this))
call FlushStoredInteger(gcSync, "0", I2S(this))
call PauseTimer(this.keychecker)
endif
endif
endif
endmethod
static method LocalKeyAndMouseCheck takes nothing returns nothing
local thistype this = LoadInteger(htSync, GetHandleId(GetExpiredTimer()), 0)
if(GetLocalPlayer() == this.from_player)then
if(IsChatOpened() == false and IsWar3Active())then
if(IsKeyPressed(this.keycode))then
call StoreInteger(gcSync, "0", I2S(this), R2I(GetMouseWorldX()))
call StoreInteger(gcSync, "0", I2S(this+offset), R2I(GetMouseWorldY()))
call SyncStoredInteger(gcSync, "0", I2S(this))
call SyncStoredInteger(gcSync, "0", I2S(this+offset))
call FlushStoredInteger(gcSync, "0", I2S(this))
call FlushStoredInteger(gcSync, "0", I2S(this+offset))
call PauseTimer(this.keychecker)
endif
endif
endif
endmethod
endstruct
function SyncKeyFrom takes player from_player, integer keycode, boolexpr onfinish returns nothing
local SyncInstance this = SyncInstance.create()
set this.from_player = from_player
set this.keychecker = CreateTimer()
set this.synchecker = CreateTimer()
set this.keycode = keycode
set this.onfinish = onfinish
call SaveInteger(htSync, GetHandleId(this.keychecker), 0, this)
call SaveInteger(htSync, GetHandleId(this.synchecker), 0, this)
call TimerStart(this.keychecker, 0.015, true, function SyncInstance.LocalKeyCheck)
call TimerStart(this.synchecker, 0.015, true, function SyncInstance.SyncKeyCheck)
endfunction
function SyncMouseFrom takes player from_player, boolexpr onfinish returns nothing
local SyncInstance this = SyncInstance.create()
set this.synchecker = CreateTimer()
set this.onfinish = onfinish
set this.from_player = from_player
if(GetLocalPlayer()==from_player)then
call StoreInteger(gcSync, "0", I2S(this), R2I(GetMouseWorldX()))
call StoreInteger(gcSync, "0", I2S(this+SyncInstance.offset), R2I(GetMouseWorldY()))
call SyncStoredInteger(gcSync, "0", I2S(this))
call SyncStoredInteger(gcSync, "0", I2S(this+SyncInstance.offset))
call FlushStoredInteger(gcSync, "0", I2S(this))
call FlushStoredInteger(gcSync, "0", I2S(this+SyncInstance.offset))
endif
call SaveInteger(htSync, GetHandleId(this.synchecker), 0, this)
call TimerStart(this.synchecker, 0.03125, true, function SyncInstance.SyncMouseCheck)
endfunction
function SyncKeyAndMouseFrom takes player from_player, integer keycode, boolexpr onfinish returns nothing
local SyncInstance this = SyncInstance.create()
set this.from_player = from_player
set this.keychecker = CreateTimer()
set this.synchecker = CreateTimer()
set this.keycode = keycode
set this.onfinish = onfinish
call SaveInteger(htSync, GetHandleId(this.keychecker), 0, this)
call SaveInteger(htSync, GetHandleId(this.synchecker), 0, this)
call TimerStart(this.keychecker, 0.015, true, function SyncInstance.LocalKeyAndMouseCheck)
call TimerStart(this.synchecker, 0.015, true, function SyncInstance.SyncMouseCheck)
endfunction
endlibrary
Описание
function SyncKeyFrom takes player from_player, integer keycode, boolexpr onfinish returns nothing
//вызывает переданный boolexpr после того как срабатывает синхронизация нажатия from_player кнопки keycode (https://docs.microsoft.com/ru-ru/windows/win32/inputdev/virtual-key-codes), игрока from_player можно достать в глобалке g_syncplayer
//переданный boolexpr уничтожайте сами
function SyncMouseFrom takes player from_player, boolexpr onfinish returns nothing
//вызывает переданный boolexpr после того как срабатывает синхронизация позиции мыши from_player
//игрока from_player можно достать в глобалке g_syncplayer, координаты мыши на местности - в g_syncmx и g_syncmy
//переданный boolexpr уничтожайте сами
function SyncKeyAndMouseFrom takes player from_player, integer keycode, boolexpr onfinish returns nothing
//вызывает переданный boolexpr после того как срабатывает синхронизация координат мыши игрока from_player
// синхронизирует координаты мыши игрока from_player в момент нажатия keycode
//(https://docs.microsoft.com/ru-ru/windows/win32/inputdev/virtual-key-codes)
//игрока from_player можно достать в глобалке g_syncplayer, координаты мыши на местности - в g_syncmx и g_syncmy
//переданный boolexpr уничтожайте сами
Использовать можно например так:
library SyncTest initializer Init_SyncTest
globals
integer KEYCODE_Q = 0x51
integer KEYCODE_W = 0x57
integer KEYCODE_E = 0x45
integer KEYCODE_R = 0x52
endglobals
function PlayerButton takes nothing returns nothing
call BJDebugMsg("Player("+I2S(GetPlayerId(g_syncplayer))+") pressed his button!")
call CreateUnit(g_syncplayer, 'Hpal', g_syncmx, g_syncmy, 0.)
endfunction
function SyncInit takes nothing returns nothing
call SyncKeyAndMouseFrom(Player(0), KEYCODE_Q, Condition(function PlayerButton))
call SyncKeyAndMouseFrom(Player(1), KEYCODE_W, Condition(function PlayerButton))
call SyncKeyAndMouseFrom(Player(2), KEYCODE_E, Condition(function PlayerButton))
call SyncKeyAndMouseFrom(Player(3), KEYCODE_R, Condition(function PlayerButton))
call BJDebugMsg("SyncInit")
call BJDebugMsg("press Q/W/E/R to spawn Paladin on cursor position!")
endfunction
function Init_SyncTest takes nothing returns nothing
call TimerStart(CreateTimer(), 2, false, function SyncInit)
endfunction
endlibrary
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован N1ghtSiren
Отредактирован Ev3nt
Отредактирован host_pi
14000 строк controlc.com/943dd66c