Page 4 of 10

Re: Automatic 'login' script

Posted: Mon Jun 10, 2013 7:59 am
by dx876234
There is a race condition in the window reservation which results in random login failures, particularly when starting multiple windows simultaneously.

This is due to the reserveActiveWindow() using a "check file" loop followed by a make file which is absolutely not atomic, ie two logins can compete for the lock. Although it sounds like a rare condition I get into it approx every 3rd time starting my 6 client script.

I would recommend using the mkdir command instead, this is atomic.

So loop on mkdir, if successful u got the lock, else loop again with appropriate timeout of course, The dead process test can be done by checking the creation time of the directory, pretty much same as done w. the file today.

Just my 5 cents :)

Best regards
DX

Re: Automatic 'login' script

Posted: Mon Jun 10, 2013 8:35 am
by rock5
If you are having problems then I welcome your feedback as I didn't get to stress test it.

I think I understand what you are saying. If 2 MMs exit the loop at the same time then one will succeed and the other will fail. I assumed that if 1 starts writing to the file that it would somehow become locked to other processes writing to it, but I just checked, 2 MMs can be writing to the file at the same time. So something has to be done to make it more reliable.

I'm not sure how mkdir comes into it. How would you record which MM has the active control? How do you read the creation time of a file from MM?

The idea I had was, after it writes to the file it reads it, maybe after a small pause. If it still has control then it continues. If another processes pid is recorded then it returns to the loop to wait again.

What do you think?

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 2:13 am
by dx876234
No matter how u twist and turn it u cant make a locking mechanism in Lua file module, its simply not atomic and u will get race condition, u can make the chances small but not remove them totally. The mkdir/rmdir Windows commands (and underlying api) are atomic on the other hand, they either fail or complete and return code indicates state so we can use them for making a locking mechanisms without race conditions.

Since lua doesnt have much of a "check attributes of files and directories" api (except in lfs) we need to keep your timeout mechanism with a separate file for overriding the lock in case of stale locks.

I also modified the backoff rest to be random for more gracefully backoff.

I typically use Lua module lfs for this but thats not a good idea here as its an external "c" module, so attached is my suggestion. I've tested it as far as I can with servers down :)

regards
dx

EDIT: Damn, the timeout mechanism will still have a race, what if we read the timeout file, find it stale and remove it, but in between another process has done the same - then we have a race. So, the timeout file should have a separate lock i guess..which again opens up for deadlocks lol

EDIT: Ops forgot to make the timeout file....

Code: Select all

local function lockdir(lockPath)
	return os.execute("mkdir "..string.fixPath(lockPath, false).." 2> nul")
end

local function unlockdir(lockPath)
	os.execute("rmdir "..string.fixPath(lockPath, false))
end

local function reserveActiveWindow()
	local lockPath = getExecutionPath().."/../micromacro.lck"
	local lock = lockdir(lockPath)

	-- Wait for if other bot needs active window
	local printed = false
	while lock ~= 0 do
		file = io.open(getExecutionPath().."/../micromacro.pid", "r");
		if file then
			raw = file:read() or ""
			file:close()
			apid, atime = string.match(raw,"pid:(.*) time:(.*)")
			apid = tonumber(apid)
			atime = tonumber(atime)
			if apid == nil or apid == pid or os.time()-tonumber(atime) > 60 then
				releaseActiveWindow()
 			else
				if not printed then
					printf("Waiting for another bot to release the active window.")
					printed = true
				else
					printf(".")
				end
			end
		end
		yrest(math.random(3000))
		lock = lockdir(lockPath)
	end 
	if printed then printf("\n") end
end

local function releaseActiveWindow()
	os.remove(getExecutionPath().."/../micromacro.pid")
	unlockdir(getExecutionPath().."/../micromacro.lck")
end

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 3:28 am
by dx876234
New locking, take 2 :)

-dx

Code: Select all

local function lockdir(lockPath)
	return (os.execute("mkdir "..string.fixPath(lockPath, false).." 2> nul") == 0)
end

local function unlockdir(lockPath)
	os.execute("rmdir "..string.fixPath(lockPath, false))
end

local function setTimeout(pidPath)
	file, err = io.open(pidPath, "w");
	if( not file ) then
		error(err, 0);
	end

	file:write("pid:"..getHwnd().." time:"..os.time())
	file:close()
end

local function getTimeout(pidPath)
	file = io.open(pidPath, "r");
	if file then
		raw = file:read() or ""
		file:close()
		apid, atime = string.match(raw,"pid:(.*) time:(.*)")
		apid = tonumber(apid)
		atime = tonumber(atime)
		if apid and atime then
			return apid, atime
		end
	end
	return nil, nil
end

local function releaseActiveWindow()
	os.remove(getExecutionPath().."/../micromacro.pid")
	unlockdir(getExecutionPath().."/../micromacro.lck")
end

local function reserveActiveWindow()
	local lockPath = getExecutionPath().."/../micromacro.lck"
	local pidPath  = getExecutionPath().."/../micromacro.pid"
	local lock = lockdir(lockPath)
	local pid = getHwnd()

	-- Wait for if other bot needs active window
	local printed = false
	while not lock do
		local atime, atime = getTimeout(pidPath)
		
		-- If nil, we prolly interupted another process between creating and writing
		if apid and atime then
			if apid == pid or os.time()-tonumber(atime) > 60 then
				printf("\nLock stale, cleaning up.")
				releaseActiveWindow()
			else
				if not printed then
					printf("Waiting for another bot to release the active window.")
					printed = true
				else
					printf(".")
				end
			end
		end
		yrest(math.random(3000))
		lock = lockdir(lockPath)
	end 
	if printed then printf("\n") end

	-- Timestamp the active window
	setTimeout(pidPath)
end

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 4:24 am
by rock5
I was testing the mkdir command. It returns 3 values. The first one is nil or true. It never equals 0 so the locking function always returns false. I wonder if the returned values might depend on the system. Also the directory name needs to be surrounded by quotes for paths with spaces.

This worked for me.

Successfully created the directory.

Code: Select all

Command> print(os.execute("mkdir \""..string.fixPath(getExecutionPath().."/../mi
cromacro.lck\"", false).." 2> nul") == true)
true
Directory already exists so failed

Code: Select all

Command> print(os.execute("mkdir \""..string.fixPath(getExecutionPath().."/../mi
cromacro.lck\"", false).." 2> nul") == true)
false
If we can rely on only one process creating the folder do we even need the pid file? Or maybe we do, to check the age.

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 8:40 am
by dx876234
Well, the lock might be stale if a process gets killed while holding the lock, its a rare event but might happen. Another way would be to put in a timer and remove lock if its not gone in 60s, assuming all multi-login sequences will be faster than that. Assuming a login uses 10s this leaves room for 6 client logins, ppl who do more prolly know enough to increase the limit.

The return value was different, I get as below. Im using Windows7 64b and Lua 5.1 (5.2 missing so much support for extensions)
Command> print(os.execute("mkdir tt"))
0
Command> print(os.execute("mkdir tt"))
A subdirectory or file tt already exists.
1
Command> print(type(os.execute("mkdir tt")))
number
Command>
-dx

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 10:35 am
by rock5
Here are my exact same commands.

Code: Select all

Command> print(os.execute("mkdir tt"))
true    exit    0
Command> print(os.execute("mkdir tt"))
A subdirectory or file tt already exists.
nil     exit    1
Command> print(type(os.execute("mkdir tt")))
A subdirectory or file tt already exists.
nil
I'm using 5.2 though. I don't know if something changed in MM.

Just found and old version of 1.04. The display is the same as yours.

It would definitely have to support the latest MM. We could easily support both by doing.

Code: Select all

local function lockdir(lockPath)
   local code = os.execute("mkdir \""..string.fixPath(lockPath, false).."\" 2> nul")
   return (code == 0 or code == true)
end

Re: Automatic 'login' script

Posted: Tue Jun 11, 2013 11:43 am
by dx876234
Agree, need support for both versions.

-dx

Re: Automatic 'login' script

Posted: Wed Jun 26, 2013 2:48 am
by C3PO
Hello,

I always get stuck while it should select the char (1-8). I figured out that the function gameState returns 0 instead of 2.

Re: Automatic 'login' script

Posted: Wed Jun 26, 2013 3:21 am
by BillDoorNZ
C3PO wrote:Hello,

I always get stuck while it should select the char (1-8). I figured out that the function gameState returns 0 instead of 2.
you didnt happen to disable the zoom that happens when you get to the character selection screen did you? from memory there is a setting in one of the interface/login/*.lua files to do this. when it is set, the gameState returns 0 instead of 2

Re: Automatic 'login' script

Posted: Wed Jun 26, 2013 6:52 am
by C3PO
I'm not sure if I understand what you mean ...

Should I set fastLoginNoZoom to false? is it that what you wanted to tell me?

Re: Automatic 'login' script

Posted: Wed Jun 26, 2013 8:19 am
by rock5
That's what he meant, yes. It needs to zoom in. This was reported before. I don't think I came up with fix for it. It shouldn't be a problem though, it zooms in pretty quickly.

Re: Automatic 'login' script

Posted: Wed Jun 26, 2013 9:59 am
by C3PO
oh its not a problem, if I know that I can disable it again.

thx for the fast replies

Re: Automatic 'login' script

Posted: Mon Jul 01, 2013 5:36 am
by C3PO
Hi, it's me again ;)

I have 2 questions:
  • is it possible to minimize the rom-window within the wp-script?
    is it possible to close all windows, or better to end the processes (MM and RoM)
thanks

Re: Automatic 'login' script

Posted: Mon Jul 01, 2013 10:28 am
by rock5
C3PO wrote:minimize the rom-window within the wp-script?
  • Code: Select all

    showWindow(getWin(), sw.minimize);
C3PO wrote:is it possible to close all windows
Do you mean minimize all games? You would have to search for the other clients that the current bot is not attached to.

Code: Select all

windows = findWindowList("*", "Radiant Arcana")
for k,window in pairs(windows) do
    showWindow(window, sw.minimize);
end
C3PO wrote:or better to end the processes (MM and RoM)
TASKKILL works well enough

Code: Select all

pid = findProcessByWindow(window)
os.execute("TASKKILL /PID " .. pid .. " /F")
The same could be done with MM consoles but you would have to make sure that the console doing the closing is the last one.

Code: Select all

windows = findWindowList("*", "ConsoleWindowClass") -- Note this will match Window command consoles too.
for k,window in pairs(windows) do
    if window ~= getHwnd() -- Does not equal current mm window
        pid = findProcessByWindow(window)
        os.execute("TASKKILL /PID " .. pid .. " /F")
    end
end
-- end current mm window
pid = findProcessByWindow(getHwnd())
os.execute("TASKKILL /PID " .. pid .. " /F")
I think that covers everything.

For more information check out the 'process' section of the micromacro wiki.
http://www.solarstrike.net/wiki/index.php5?title=Manual

Re: Automatic 'login' script

Posted: Tue Jul 23, 2013 11:30 am
by C3PO
Hello,

I've got another problem... From time to time the script hangs up because the Choose Server Frame is displayed. Am I doing something wrong or is there a solution for that ...

Re: Automatic 'login' script

Posted: Tue Jul 23, 2013 11:50 am
by rock5
The latest version of fastlogin fises that. Try updating.

Re: Automatic 'login' script

Posted: Wed Jul 24, 2013 10:30 am
by hagenleu
Hi Rock5 (long time no see!)

I successfully setup your fast login (108 char version) and bot is running as intendend (no error from the first to the last character in the account).

Also successfully setup this automatic login script; from micromacro window with the following string bot run with no error:

Code: Select all

rom/login acc:1 char:1 path:tier2
But when i try to make a bat file tier2.bat with the following code:

Code: Select all

START micromacro.exe scripts\rom\login acc:1 char:1 path:tier2
micromacro give me the error in the attached image.

I'm sure i'm mistaking the sintax, but i can't found the right one, can you give it a look and tell me in what i'm wrong?

thx

Re: Automatic 'login' script

Posted: Wed Jul 24, 2013 11:37 am
by lolita
As rock5 said in first post
rock5 wrote: Examples:
  • From the Micromacro console

Code: Select all

rom/login acc:48 char:2 client:rom4u path:cot_tele
  • Or starting multiple clients from a batch file

Code: Select all

FOR /F "tokens=1 delims=" %%A in ('cd') do SET folder=%%A
START ../../micromacro.exe "%folder%/login.lua" acc:45 char:1 client:rom4u path:path1
START ../../micromacro.exe "%folder%/login.lua" acc:46 char:1 client:rom4u path:path2
START ../../micromacro.exe "%folder%/login.lua" acc:47 char:1 client:rom4u path:path3
so in your case batch file would be

Code: Select all

FOR /F "tokens=1 delims=" %%A in ('cd') do SET folder=%%A
START ../../micromacro.exe "%folder%/login.lua" acc:1 char:1 client:rom path:tier2

Re: Automatic 'login' script

Posted: Tue Jul 30, 2013 6:11 am
by Alleexx
I have 2 runes of magic clients. One for manual play and one with ultra model files. This works great when I'm starting the clients manually and then start the bot but weird things happen when I try to do it with login script.
Even if the ultra model files are not installed on my manual client, when I start it with the bot it starts without textures anyways. When I start it manually it works though. I have checked the shortcuts and they are correct. When I start my model client for manual play and then start the non-model client with the bot the textures works. So somehow the bot manages to start a client with model files that doesn't exist in that folder.
Also any time I start a client with the bot the settings in game resets. As example it always changes graphics from low to medium.

The clients are installed in C:\Program Files\Runes of Magic\Client.exe and C:\Program Files (x86)\Runes Of Magic\Client.exe