User File #73828312429327742

Upload All User Files

#73828312429327742 - DK64 sync-savestates.lua

dk64-sync-savestates.lua
Game: Donkey Kong 64 ( N64, see all files )
168 downloads
Uploaded 8/29/2021 8:41 PM by Zinfidel (see all 12)
Sync script modified to work on very old versions of BizHawk, with code specific to syncing DK64.
package.path = package.path .. ';luasocket/lua/?.lua;luasocket/lua/socket/?.lua'
package.cpath = package.cpath .. ';luasocket/?.dll;luasocket/mime/?.dll;luasocket/socket/?.dll'
local socket = require 'socket'

local tab = {}                     -- entire parsed file broken into lines
local gap_minimum = 10			   -- Set the minimum number of frames between savestates. This can help prevent thrashing in bad sections.

function ParseFile()
	print("Parsing file...")
	local logfile = io.open("log.txt", "r")

	local char
	repeat
		char = logfile:read("*l")
		table.insert(tab, char)
	until char == nil

	logfile:close()
	print("File parsed! Line count: "..#tab.."\n")
end

local function fl(val)
	return mainmemory.readfloat(val, true)
end

local function sigfig(value, figs)
	str = string.format("%f", value)
	return str:sub(1, figs+1) -- account for '.' character
end

u32 = mainmemory.read_u32_be

-- Customize this function to retrieve your sync data and format it the same way as it appears in log.txt. An example of how
-- to do this for N64 Star Wars Episode 1: Racer, matching the example in sync-collect.lua, is the default implementation. this
-- value will get compared, as a string, to the corresponding line in log.txt to verify sync.
function GetData()

	-- get pointers
	local player = u32(0x7FBB4C)
	local camera = u32(0x7FB968)
	
	-- the actual values are floats
	-- but let's just use their raw hex view for simplicity
	if player >= 0x80000000 and player < 0x80800000 then
		player = player - 0x80000000
		pos = string.format("%s,%s,%s",
			sigfig(fl(player + 0x7C), 8), -- x
			sigfig(fl(player + 0x80), 8), -- y
			sigfig(fl(player + 0x84), 8)) -- z
	else
		pos = "00000000,00000000,00000000"
	end
	
	if camera >= 0x80000000 and camera < 0x80800000 then
		camera = camera - 0x80000000
		cam = string.format("%s,%s,%s",
			sigfig(fl(camera + 0x1FC), 8), -- x
			sigfig(fl(camera + 0x200), 8), -- y
			sigfig(fl(camera + 0x204), 8)) -- z
	else
		cam = "00000000,00000000,00000000"
	end
	
	-- conbined string
	return pos.. "," .. cam

end

-- Pause the client and establish a tcp connection to the server.
client.pause()
local tcp = assert(socket.connect("127.0.0.1", 45678), "Socket client failed to establish tcp connection with server. Script aborted.")
console.write("Connection to server established!\n")
tcp:settimeout(2)
emu.yield()

ParseFile()

local onExitEvent = event.onexit(
	function()
		console.log("\nScript exiting. Sending quit command to server." );
		tcp:send("quit\n")
	end
	, "OnExit" );

local function endsWith(str, ending)
	return ending == "" or str:sub(-#ending) == ending
end

local function trimNewLine(str)
	return string.sub(str, 1, string.len(str) - 1)
end

local last_ss = 0

while true do
	local frame = emu.framecount()
	local original = tab[frame+1]
	local yours = GetData()

	gui.pixelText(0, 40, string.format( "%s - original\n%s - yours", original, yours))

	-- mismatch
	if (yours ~= original) and ((frame - last_ss) > gap_minimum) then
		last_ss = frame
		client.pause()

		-- screen and console alarm
		gui.pixelText(0, 40, string.format( "%s - original\n%s - yours\ndesync at frame %d", original, yours, frame), "red")
		print(string.format( "desync at frame %d\n%s\n%s\n", frame, yours, original))
		emu.yield()

		local response = ""
		tcp:send(frame .. ".State\n")
		console.write("Sent packet asking for frame " .. tostring(frame) .. " ...")
		emu.yield()
		while true do
			local line, err, partial = tcp:receive()
			if not err then
				response = line
				console.log("\nReceived message that savestate " .. tostring(frame) .. " is available. Loading ...")
				emu.yield()
				savestate.load("states/" .. response)
				client.unpause()
				break
			elseif err == "timeout" then
				response = response .. partial
				console.write('.')
			elseif err == "closed" then
				console.log("\nConnection with server closed. Exiting now.")
				break
			else
				console.log("\nUnknown communication error.")
			end

			emu.yield()
		end

	end

	-- so far so good
	emu.frameadvance()
end