Design question for UDP comms

Runes of Magic/Radiant Arcana (http://www.runesofmagic.com)
Post Reply
Message
Author
BillDoorNZ
Posts: 446
Joined: Wed Aug 03, 2011 7:37 pm

Design question for UDP comms

#1 Post by BillDoorNZ » Thu Aug 11, 2011 6:38 pm

Hiyas,

I play at work a lot and have my game window resized very small so I can just see whats going on - but cannot read names / chats etc. So I usually have the bot grinding out waypoints, or just sitting there so I can keep an eye on guild chat etc (I resize to make the font large enough to read and then sit it behind a 'always on top' window - process explorer - so I can only see the chat portion). Unfortunately this is still relatively noticeable and I'd rather not have the rom window visible at all (in fact I hide it completely off task bars etc too).

Anyway. I figured to do something about it and write a .net app that has tabs for each chat channel and somehow get rom bot to send me the chats - I use the newish Monitor stuff to get rom to get all chat messages. This is easy enough, but it took me a while to get a UDP client working to do the IPC stuff to get the chats back to my .net app (being very new to lua) and now I'm using luasocket library to do this and have a UDP server in my .net app - this all works okish - I have the monitors starting up via the onLoad event of my characters profile. i.e.:

Code: Select all

	<onLoad><![CDATA[
		-- Additional Lua code to execute after loading the profile
		-- and before the bot starts. e.g. You could overwrite profile settings here
		-- like: changeProfileOption("HP_REST", 60);
		--registerTimer("AutoCombat", 11, SendF11);
		startChatMonitors();
		registerTimer("ChatMonitor", 500, monitorChats);
	]]></onLoad>


function startChatMonitors()
	EventMonitorStart("allGuild", "CHAT_MSG_GUILD")
	EventMonitorStart("allSay", "CHAT_MSG_SAY")
	EventMonitorStart("allWhisper", "CHAT_MSG_WHISPER")
	EventMonitorStart("allZone", "CHAT_MSG_ZONE")
	EventMonitorStart("allWorld", "CHAT_MSG_YELL")
	EventMonitorStart("allParty", "CHAT_MSG_PARTY")
	EventMonitorStart("allTrade", "CHAT_MSG_TRADE")
	EventMonitorStart("allCombat", "CHAT_MSG_COMBAT")
	EventMonitorStart("allGM", "CHAT_MSG_GM")
	EventMonitorStart("allSale", "CHAT_MSG_SALE")
	EventMonitorStart("allSystem", "CHAT_MSG_SYSTEM")
end

function monitorChats()
	c = socket.udp()
	c:settimeout(0)
	c:setpeername('192.168.100.128', 60000)
	
	--cprintf(cli.red, "checking guild")
	printEvents("Guild", cli.green)
	printEvents("Whisper", cli.turquoise)
	printEvents("Say", cli.white)
	printEvents("World", cli.red)
	printEvents("Zone", cli.purple)
	printEvents("Party", cli.lightgreen)
	printEvents("Trade", cli.lightblue)
	printEvents("Combat", cli.lightgray)
	printEvents("GM", cli.lightblue)
	printEvents("Sale", cli.lightblue)
	printEvents("System", cli.lightgray)

	c:close()
end

function printEvents(channel, color)
	local _time, more, msg, name = EventMonitorCheck("all"..channel)
	repeat
		if (_time ~= nil) then 
			cprintf(color, "[%s - %s] %s: %s\n", channel, os.date(), getPlayerNameFromLink(name), stripColorCodes(msg)) 
			local st = sprintf("%s\1%s\1%s\1%s", channel, getPlayerNameFromLink(name), stripColorCodes(msg), os.date())
			c:send(st)
		end
		if (more) then _time, more, msg, name = EventMonitorCheck("all"..channel) end
	until (not more)
end

function getPlayerNameFromLink(playerLink)
	if playerLink == "" or playerLink == nil then
		return "";
 	end

	local s,e, name = string.find(playerLink, "|Hplayer:(.+).*|h%[");
	name = name or "<unknown>";

	return name;
end

function stripColorCodes(source)
	if (source == "") or (source == nil) then
		return "";
	end
	
	source = string.gsub(source, "|H|h|c%x%x%x%x%x%x%x%x", "");
	source = string.gsub(source, "|c%x%x%x%x%x%x%x%x", "");
	return string.gsub(source, "|h", "");
end

Once I had this working I then added a UDP server in the rom bot:

Code: Select all


romSocket = socket.udp()
romSocket:setsockname('*', 60002)
	
function listenForMessages()
	romSocket:settimeout(0)
	local recv = romSocket:receive()
	if (recv ~= nil) then
		local res = SplitString(recv, '\1', 4)
		local style = string.upper(res[1])
		local channel = string.upper(res[2])
		local p1 = res[3]
		local p1 = string.gsub(res[3], "'", "")
		local p2 = nil
		
		if (style=="CHAT") then
			cprintf(cli.white, "Channel: %s Message: %s\n", channel, p1);
			cprintf(cli.white, "Sending [%s] %s\n", channel, p1);
			if (not(channel == nil or channel =="")) then
				if ((channel == "WHISPER") and not(p2 == nil or p2 == "")) then
					gameText(p1,channel,p2);
				else
					gameText(p1,channel);
				end
			end
		else 
			if (style == "COMMAND") then
				funct=loadstring(p1)
				if type(funct) == "function" then
					local status,err = pcall(funct);
					if status == false then
						printf("onLoad error: %s\n", err);
					end

				else
					print ("Invalid Command")
				end		
			end			
		end
	end
end

function SplitString(str, delim, maxNb)
    -- Eliminate bad cases...
    if string.find(str, delim) == nil then
        return { str }
    end
    if maxNb == nil or maxNb < 1 then
        maxNb = 0    -- No limit
    end
    local result = {}
    local pat = "(.-)" .. delim .. "()"
    local nb = 0
    local lastPos
    for part, pos in string.gfind(str, pat) do
        nb = nb + 1
        result[nb] = part
        lastPos = pos
        if nb == maxNb then break end
    end
    -- Handle the last field
    if nb ~= maxNb then
        result[nb + 1] = string.sub(str, lastPos)
    end
    return result
end

As you can see its all very primitive code and not exactly elegant or particularly efficient and reflects my n00bness to LUA.

This all works ok, but I'm questioning the way I have implemented it all via timers and am wondering if there is a better way of doing this? i.e. a better design.

User avatar
lisa
Posts: 8332
Joined: Tue Nov 09, 2010 11:46 pm
Location: Australia

Re: Design question for UDP comms

#2 Post by lisa » Thu Aug 11, 2011 9:59 pm

I would be tempted to create a game addon for this and not worry about using the bot for it at all.

To give you a little idea this is one I use for DoD first boss. When he says "I'll crush you!" I have the characters follow party 1.

Have a browse of the code and you should be able to pick it up quickly.
madman.zip
(24.87 KiB) Downloaded 124 times

--=== Note to others reading ===--

This addon isn't perfect for dod first boss so don't use it unless you also know how to deal with the other issues that come from using it.
I dont want to see 20 posts by people asking how to make it work for dod.
I added it here purely as an example for BillDoorNZ to see how he could use an addon instead for what he is doing in bot for a better result.
Remember no matter you do in life to always have a little fun while you are at it ;)

wiki here http://www.solarstrike.net/wiki/index.php?title=Manual

BillDoorNZ
Posts: 446
Joined: Wed Aug 03, 2011 7:37 pm

Re: Design question for UDP comms

#3 Post by BillDoorNZ » Thu Aug 11, 2011 10:23 pm

hmm, I see how your addon works, but it doesn't really help me, in that I'm trying to send all chat off to my external application and have it then send chat messages back (as I type them).

The outline you have sent through is nice - if I was trying to do something in-game, but I'm really just using it to allow me to chat from an external app (via UDP) instead of having to do it directly in the game-client.

User avatar
lisa
Posts: 8332
Joined: Tue Nov 09, 2010 11:46 pm
Location: Australia

Re: Design question for UDP comms

#4 Post by lisa » Fri Aug 12, 2011 12:05 am

Ahh I didn't realise you wanted to also send chat to in game, thought you were just trying to monitor the chat, Hmm
Remember no matter you do in life to always have a little fun while you are at it ;)

wiki here http://www.solarstrike.net/wiki/index.php?title=Manual

User avatar
Administrator
Site Admin
Posts: 5313
Joined: Sat Jan 05, 2008 4:21 pm

Re: Design question for UDP comms

#5 Post by Administrator » Fri Aug 12, 2011 1:36 am

Well, you could use timers or coroutines. I think the coroutine route would work better for networking stuff.

Code: Select all

function networkThread()
  while(true) do
    -- this is your main loop for just networking stuff.


    coroutine.yield();
  end
end

createThread("networkThread", networkThread);

BillDoorNZ
Posts: 446
Joined: Wed Aug 03, 2011 7:37 pm

Re: Design question for UDP comms

#6 Post by BillDoorNZ » Sun Aug 14, 2011 4:11 pm

Awesome!

Thanks for that admin, just what I was looking for - am used to writing a lot of multi-threaded apps but wasn't sure what the recommended route was with lua or whether it supported threading in the traditional sense. I'd seen the coroutine stuff but didn't know how to start a new thread etc either.

All working now, tho I still have to select a waypoint file for the bot to 'run' (at the moment I just set wander radius etc to 0 and run the wander waypoint manually). I suppose my best bet there would be to create a new programmatic waypoint like the 'wander' one and just tell it not to engage in combat etc.

User avatar
Administrator
Site Admin
Posts: 5313
Joined: Sat Jan 05, 2008 4:21 pm

Re: Design question for UDP comms

#7 Post by Administrator » Sun Aug 14, 2011 5:25 pm

It is important to remember that coroutines are not real threads; they do not run concurrently. So, it is important to call coroutine.yield() (or yrest()) in your main "thread" to give your coroutines time to run. createThread() is a library function that simply hides a lot of the stuff you normally don't want to bother with, such as constantly checking the state and actively telling it to run all the time.

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests