-- utils for recording packet-videos gPacketVideoFifo = CreateFIFO() gPacketVideoData = {} gPacketVideoRecording = false gPacketVideoRecording = false gPacketVideoPlaybackRunning = false gPacketVideoSpeedFactor = 1 kPacketVideoChunkType_Recv = 1 kPacketVideoChunkType_Send = 2 kPacketVideoChunkType_Pos = 3 kPacketVideoChunkType_Map = 4 kPacketVideoChunkType_Player = 5 gPacketVideoKnownBlockSend = {} gPacketVideoKnownBlockSend[kPacket_ExtBundledPacket] = true -- request party pos gPacketVideoKnownBlockSend[kPacket_Generic_Command] = true -- ?? gPacketVideoKnownBlockSend[kPacket_Take_Object] = true -- looter gPacketVideoKnownBlockSend[kPacket_Double_Click] = true -- looter gPacketVideoKnownBlockSend[kPacket_Single_Click] = true -- mobname request gPacketVideoNoPlaybackPackets = {} gPacketVideoNoPlaybackPackets[kPacket_Ping] = true gPacketVideoNoPlaybackPackets[kPacket_Accept_Movement_Resync_Request] = true gPacketVideoNoPlaybackPackets[kPacket_Block_Movement] = true gPacketVideoNoPlaybackPackets[kPacket_Target] = true gPacketVideoNoPlaybackPackets[kPacket_Open_Container] = true gPacketVideoNoPlaybackPackets[kPacket_Logout] = true -- logout gPacketVideoFileName_prefix = gPacketVideoFileName_folderpath.."myvid." gPacketVideoFileName_postfix = ".ipv" function PacketVideo_Recording_Toggle() if (gPacketVideoRecording) then PacketVideo_Recording_End() else PacketVideo_Recording_Start() end end function PacketVideo_Recording_Start() gPacketVideoFileName = gPacketVideoFileName_prefix..(os.time() or Client_GetTicks())..gPacketVideoFileName_postfix PacketVideo_ClearData() gPacketVideoRecording = true PacketVideo_LogPlayer(GetPlayerSerial()) PacketVideo_LogMap(MapGetMapIndex()) PacketVideo_LogPlayerPos(gPlayerXLoc,gPlayerYLoc,gPlayerZLoc,gPlayerDir) Send_ClientQuery(gRequest_States,GetPlayerSerial()) Send_Movement_Resync_Request() end function PacketVideo_Recording_End() gPacketVideoRecording = false end function PacketVideo_Playback() if (not gPacketVideoData[1]) then return end job.create(function() local oldpos = {gPlayerXLoc,gPlayerYLoc,gPlayerZLoc,gPlayerDir} local oldserial = GetPlayerSerial() local oldfacet = MapGetMapIndex() gPlayerBodySerial = nil local lastframet local lastrealt local lastwaitrealt print("### packetvideo playback start",#gPacketVideoData,gPacketVideoSpeedFactor) gPacketVideoPlaybackRunning = true local playback_start_t = Client_GetTicks() local record_start_t = gPacketVideoData[1].t local forcedwaitinterval = 250 -- to ensure that something is visible, even if the processor is totally busy local nextforcedwaitt = Client_GetTicks() + forcedwaitinterval local minwait = 5 local kMax = #gPacketVideoData local bPlayerMobileStarted = false for k,chunk in pairs(gPacketVideoData) do local realt = Client_GetTicks() --~ local framet = chunk.t - record_start_t + playback_start_t --~ if (framet > t) then job.wait(framet-t) end local framet = chunk.t local playermobile = GetPlayerMobile() if (playermobile) then bPlayerMobileStarted = true end --~ if (k > 200) then os.exit(0) end local debugname = "unknown" if (chunk.chunktype == kPacketVideoChunkType_Player) then debugname = "player" end if (chunk.chunktype == kPacketVideoChunkType_Map) then debugname = "map" end if (chunk.chunktype == kPacketVideoChunkType_Pos) then debugname = "pos" end if (chunk.chunktype == kPacketVideoChunkType_Send) then debugname = "send" end if (chunk.chunktype == kPacketVideoChunkType_Recv) then debugname = "recv:"..(gPacketTypeId2Name[chunk.data:PeekNetUint8(1+4+4)] or "???") end print("### packetvideo playback step, frame=",k.."/"..kMax,"wait=",framet,chunk.chunktype,debugname,"gPlayerBodySerial=",gPlayerBodySerial,"playermobile=",playermobile) if (bPlayerMobileStarted) then assert(playermobile) end local maxwait = 5*1000 if (lastframet) then local wait1 = (framet - lastframet) local wait2 = floor(wait1 / gPacketVideoSpeedFactor) if (lastrealt) then wait2 = wait2 - (realt - lastrealt) end if (wait2 > minwait or realt > nextforcedwaitt) then job.wait(max(1,min(maxwait,wait2))) nextforcedwaitt = realt + forcedwaitinterval end end lastframet = framet lastrealt = realt if (chunk.chunktype == kPacketVideoChunkType_Recv) then local headlen = 1+4+4 local iId = chunk.data:PeekNetUint8(headlen) if (not gPacketVideoNoPlaybackPackets[iId]) then local fifo = CreateFIFO() fifo:PushFIFOPartRaw(chunk.data,headlen) --~ print("playback packet",chunk.data:Size(),fifo:Size()) local iPacketSize = fifo:Size() HandlePacket(fifo,iId,iPacketSize) fifo:Destroy() end elseif (chunk.chunktype == kPacketVideoChunkType_Send) then elseif (chunk.chunktype == kPacketVideoChunkType_Pos) then local xloc,yloc,zloc,fulldir = unpack(chunk.data) SetPlayerPos(xloc,yloc,zloc,fulldir,true) if (gCurrentRenderer == Renderer3D) then gCurrentRenderer:NotifyPlayerTeleported() gTileFreeWalk:SetPosFromPacketVideo(xloc,yloc,zloc,fulldir) end elseif (chunk.chunktype == kPacketVideoChunkType_Map) then local mapindex = unpack(chunk.data) MapChangeRequest(mapindex) elseif (chunk.chunktype == kPacketVideoChunkType_Player) then local verion,serial = unpack(chunk.data) gPlayerBodySerial = serial end end gPacketVideoPlaybackRunning = false print("### packetvideo playback end") gPlayerBodySerial = oldserial MapChangeRequest(oldfacet) local xloc,yloc,zloc,fulldir = unpack(oldpos) SetPlayerPos(xloc,yloc,zloc,fulldir,true) Send_Movement_Resync_Request() Send_ClientQuery(gRequest_States,GetPlayerSerial()) end) end RegisterListener("Hook_StartInGame",function () local pv = gCommandLineSwitches["-pv"] if (pv) then local filename = gCommandLineArguments[pv+1] if (filename) then PacketVideo_Load(filename) --~ PacketVideo_Playback() end end local pv = gCommandLineSwitches["-rpv"] if (pv) then local filename = gCommandLineArguments[pv+1] if (filename) then PacketVideo_LoadRarzorPV(filename) end end end) -- ***** ***** ***** ***** ***** hooks function PacketVideo_ClearData () for k,data in pairs(gPacketVideoData) do if (data.Destroy) then data:Destroy() end -- release fifos end gPacketVideoData = {} end function MyPeek32 (fifo,offset) -- should be fifo:PeekNetUint32(1) return fifo:PeekNetUint8(offset+0) + fifo:PeekNetUint8(offset+1)*256 + fifo:PeekNetUint8(offset+2)*256*256 + fifo:PeekNetUint8(offset+3)*256*256*256 end function PacketVideo_Load (filename) if (not filename) then return false end local filename2 = gPacketVideoFileName_folderpath .. filename if (file_exists(filename2)) then filename = filename2 end if (not file_exists(filename)) then print("PacketVideo_Load:file not found",filename) return false end PacketVideo_ClearData() local fifo = CreateFIFO() fifo:ReadFromFile(filename) print("### packetvideo load",filename,fifo:Size()) local iProgressStep = 0 local quickfifo = fifo:GetQuickHandle() while fifo:Size() > 0 do local ctype = FIFO_PeekNetUint8(quickfifo,0) local t = MyPeek32(fifo,1) --~ local hex = "" iProgressStep = iProgressStep + 1 if (math.mod(iProgressStep,5000) == 0) then print("PacketVideo_Load step",fifo:Size(),ctype) end --~ for i=0,1+4+4 do hex = hex .. sprintf("%02x ",fifo:PeekNetUint8(i)) end --~ print("pv load step",ctype,t,fifo:PeekNetUint32(1+4),hex) if (ctype == kPacketVideoChunkType_Recv or ctype == kPacketVideoChunkType_Send) then local len = MyPeek32(fifo,1+4) local data = CreateFIFO() data:PushFIFOPartRaw(fifo,0,len+1+4+4) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data=data}) fifo:PopRaw(1+4+4+len) elseif (ctype == kPacketVideoChunkType_Pos) then fifo:PopRaw(1+4) local xloc = FIFO_PopNetUint16(quickfifo) local yloc = FIFO_PopNetUint16(quickfifo) local zloc = FIFO_PopNetInt16(quickfifo) local fulldir = FIFO_PopNetUint8(quickfifo) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={xloc,yloc,zloc,fulldir}}) elseif (ctype == kPacketVideoChunkType_Map) then fifo:PopRaw(1+4) local mapindex = FIFO_PopNetUint8(quickfifo) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={mapindex}}) elseif (ctype == kPacketVideoChunkType_Player) then fifo:PopRaw(1+4) local version = FIFO_PopNetUint8(quickfifo) local serial = FIFO_PopNetUint32(quickfifo) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={version,serial}}) else print("unknown ctype:",ctype) break end end print("PacketVideo_Load ok, cleaning up") fifo:Destroy() print("PacketVideo_Load finished") return true end function Generate_kPacket_Naked_MOB (fifo,serial,artid,xloc,yloc,zloc,dir,hue,flag,notoriety) fifo:PushNetUint8( kPacket_Naked_MOB ) fifo:PushNetUint32( serial ) fifo:PushNetUint16( artid ) fifo:PushNetUint16( xloc ) fifo:PushNetUint16( yloc ) fifo:PushInt8( zloc ) fifo:PushNetUint8( dir ) fifo:PushNetUint16( hue ) -- hue/skin color fifo:PushNetUint8( flag ) fifo:PushNetUint8( notoriety ) end -- table.insert(rpv.worlddata_items,item) function Generate_kPacket_Equipped_MOB (fifo_out,serial,artid,xloc,yloc,zloc,dir,hue,flag,notoriety,equipitems) local fifo = CreateFIFO() fifo:PushNetUint32( serial ) fifo:PushNetUint16( artid ) fifo:PushNetUint16( xloc ) fifo:PushNetUint16( yloc ) -- the usage of this and on which servers it occurs on is unknown if (TestBit(xloc,0x8000)) then fifo:PushNetUint16(-1) end -- dir2 ? fifo:PushInt8( zloc ) fifo:PushNetUint8( dir ) fifo:PushNetUint16( hue ) -- hue/skin color fifo:PushNetUint8( flag ) fifo:PushNetUint8( notoriety ) for k,item in pairs(equipitems) do fifo:PushNetUint32(item.serial) fifo:PushNetUint16(item.artid) fifo:PushNetUint8(item.layer) if (TestBit(item.artid,0x8000)) then fifo:PushNetUint16(item.hue) end end fifo:PushNetUint32(0) -- finish packet header for dynamic size fifo_out:PushNetUint8( kPacket_Equipped_MOB ) fifo_out:PushNetUint16(fifo:Size()+3) fifo_out:PushFIFOPartRaw(fifo,0,fifo:Size()) fifo:Destroy() end function PacketVideo_LoadRarzorPV (filename) if (not filename) then return false end print("PacketVideo_LoadRarzorPV",filename) local rpv = LoadRazorPacketVideo(filename) if (not rpv) then print("PacketVideo_LoadRarzorPV failed") return end print("PacketVideo_LoadRarzorPV:first part ok") PacketVideo_ClearData() local t = 0 local worlditems = {} for k,item in pairs(rpv.worlddata_items) do worlditems[item.serial] = item end function AddMobile (mobile,debugfrom) local fifo = CreateFIFO() local len = 17 fifo:PushUint8(kPacketVideoChunkType_Recv) fifo:PushUint32(t) fifo:PushUint32(len) --~ mobile.Name = RPV_ReadString(fifo) --~ mobile.HitsMax = fifo:PopUint16() --~ mobile.Hits = fifo:PopUint16() --~ mobile.Map = fifo:PopUint8() --~ Generate_kPacket_Naked_MOB(fifo,mobile.serial,mobile.Body,mobile.xloc,mobile.yloc,mobile.zloc,mobile.Direction,mobile.hue,mobile.flags,mobile.Notoriety) local equipitems = {} for k,serial in ipairs(mobile.items) do local razoritem = worlditems[serial] local item = {serial=serial,artid=razoritem.ItemID,hue=razoritem.hue,layer=razoritem.Layer} table.insert(equipitems,item) end Generate_kPacket_Equipped_MOB(fifo,mobile.serial,mobile.Body,mobile.xloc,mobile.yloc,mobile.zloc,mobile.Direction,mobile.hue,mobile.flags,mobile.Notoriety,equipitems) table.insert(gPacketVideoData,{chunktype=kPacketVideoChunkType_Recv,t=t,data=fifo}) print("PacketVideo_LoadRarzorPV: worlddata_mobiles",debugfrom,mobile.serial,mobile.Name) end for k,mobile in pairs(rpv.worlddata_mobiles) do AddMobile(mobile,k) end AddMobile(rpv.playerdata,"player") table.insert(gPacketVideoData,{chunktype=kPacketVideoChunkType_Player,t=t,data={1,rpv.playerdata.serial}}) table.insert(gPacketVideoData,{chunktype=kPacketVideoChunkType_Map,t=t,data={rpv.playerdata.Map}}) table.insert(gPacketVideoData,{chunktype=kPacketVideoChunkType_Pos,t=t,data={rpv.playerdata.xloc,rpv.playerdata.yloc,rpv.playerdata.zloc,rpv.playerdata.Direction}}) for k,packet in pairs(rpv.packets) do t = t + packet.iTimeSinceLastPacket if (packet.iPacketID ~= kPacket_Object_to_Object) then local fifo = CreateFIFO() local len = packet.fifo:Size() fifo:PushUint8(kPacketVideoChunkType_Recv) fifo:PushUint32(t) fifo:PushUint32(len) fifo:PushFIFOPartRaw(packet.fifo,0,len) table.insert(gPacketVideoData,{chunktype=kPacketVideoChunkType_Recv,t=t,data=fifo}) end end print("PacketVideo_LoadRarzorPV ok, cleaning up") DestroyRazorPacketVideo(rpv) print("PacketVideo_LoadRarzorPV finished") return true end function PacketVideo_LogRecv(fifo,len) if (not gPacketVideoRecording) then return end local ctype = kPacketVideoChunkType_Recv local t = Client_GetTicks() local data = CreateFIFO() data:PushUint8(ctype) data:PushUint32(t) data:PushUint32(len) data:PushFIFOPartRaw(fifo,0,len) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data=data}) if (gPacketVideoFileName) then data:AppendToFile(gPacketVideoFileName) end end function PacketVideo_LogSend(fifo,len) if (not gPacketVideoRecording) then return end local ctype = kPacketVideoChunkType_Send local t = Client_GetTicks() local data = CreateFIFO() data:PushUint8(ctype) data:PushUint32(t) data:PushUint32(len) data:PushFIFOPartRaw(fifo,0,len) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data=data}) if (gPacketVideoFileName) then data:AppendToFile(gPacketVideoFileName) end end function PacketVideo_LogPlayerPos(xloc,yloc,zloc,fulldir) if (not gPacketVideoRecording) then return end local ctype = kPacketVideoChunkType_Pos local t = Client_GetTicks() local data = CreateFIFO() data:PushUint8(ctype) data:PushUint32(t) data:PushNetUint16(xloc) data:PushNetUint16(yloc) data:PushNetInt16(zloc) data:PushNetUint8(fulldir) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={xloc,yloc,zloc,fulldir}}) if (gPacketVideoFileName) then data:AppendToFile(gPacketVideoFileName) end data:Destroy() end function PacketVideo_LogMap(mapindex) if (not gPacketVideoRecording) then return end local ctype = kPacketVideoChunkType_Map local t = Client_GetTicks() local data = CreateFIFO() data:PushUint8(ctype) data:PushUint32(t) data:PushNetUint8(mapindex) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={mapindex}}) if (gPacketVideoFileName) then data:AppendToFile(gPacketVideoFileName) end data:Destroy() end function PacketVideo_LogPlayer (serial) if (not gPacketVideoRecording) then return end local ctype = kPacketVideoChunkType_Player local t = Client_GetTicks() local data = CreateFIFO() data:PushUint8(ctype) data:PushUint32(t) data:PushNetUint8(1) data:PushNetUint32(serial) table.insert(gPacketVideoData,{chunktype=ctype,t=t,data={1,serial}}) if (gPacketVideoFileName) then data:AppendToFile(gPacketVideoFileName) end data:Destroy() end function PacketVideo_BlockSend(fifo) if (not gPacketVideoPlaybackRunning) then return false end local iId = fifo:PeekNetUint8(0) or 0 if (not gPacketVideoKnownBlockSend[iId]) then print("PacketVideo_BlockSend",sprintf("0x%02x",iId),gPacketTypeId2Name[iId],_TRACEBACK()) end return true end