Created
May 4, 2010 04:03
-
-
Save glongman/388941 to your computer and use it in GitHub Desktop.
HTML5 web socket bot for Unreal3 Bots. Includes HTML bot impl and ruby proxy server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script> | |
<script> | |
// Note: the Bot implementation is called MYBOT and is defined after UT3BOT. | |
/* UT3BOT is a Utility that handles initiating the connection to the Unreal server | |
* via a web socket. | |
* To begin, you call | |
* UT3Bot.start(server_host, name, callback) | |
* - server_name is the hostname or IP address of the Unreal Server | |
* - name is the name you want displayed for the bot on the Unreal Server | |
* - callback is a function that takes an argument. invoked when a message arrives. | |
* To send messages: | |
* UT3BOT.send_msg(command, arg1, arg2, .., argn) | |
* we take care of formatting and sending the message to the Unreal Server | |
* | |
* Limitations: | |
* - assumes an unreal.rb proxy is running on localhost, port 8080 | |
* - on connection fail UT3BOT becomes a stone cold lump. | |
*/ | |
if (!window.UT3BOT) (function() { | |
var ws = null; | |
var debug = function(str) { | |
$("#debug").append("<p>" + str + "</p>"); | |
}; | |
var ws_send = function(str) { | |
$("#msg").append("<p>[ OUT ] "+str+"</p>"); | |
ws.send(str+"\n"); | |
}; | |
var onopen = function () { | |
debug("web socket connected...waiting for unreal to say hello."); | |
}; | |
var onmessage = function(evt) { | |
$("#msg").append("<p>[ IN ] "+evt.data+"</p>"); | |
if (UT3BOT.message_handler) UT3BOT.message_handler(eval(evt.data)); | |
}; | |
var onclose = function() { | |
debug("socket closed"); | |
} | |
var connected = false; | |
var connector = function(evt, user_message_handler) { | |
msg = evt.data; | |
if (!connected) { | |
if (evt.data == 'unreal connection lost') { | |
UT3BOT.connected(null); | |
} else { | |
connected = true; | |
UT3BOT.connected(user_message_handler) | |
} | |
} else { | |
throw("Illegal State: announcement callback called after successful connection.") | |
} | |
}; | |
var connect = function(host) { | |
ws = new WebSocket("ws://localhost:8080/"+host); | |
ws.onopen = onopen; | |
ws.onmessage = onmessage; | |
ws.onclose = onclose; | |
}; | |
UT3BOT = { | |
bot_name: null, | |
message_handler: null, | |
send_msg: function() { | |
var args = Array.prototype.slice.call(arguments); | |
ws_send(args.join("|")); | |
}, | |
start: function(host, bot_name, user_message_callback) { | |
if (ws) throw "start was already called."; | |
self.bot_name = bot_name; | |
self.message_handler = function(evt) { | |
connector(evt, user_message_callback); | |
}; | |
connect(host) | |
}, | |
connected: function(callback) { | |
if (!callback) { | |
debug("The connection to unreal failed. We're borked.") | |
self.message_handler = null; | |
} else { | |
debug("unreal server said hello.. starting."); | |
self.message_handler = callback | |
self.send_msg("INIT", self.bot_name,"IronGuard","0"); | |
} | |
} | |
} | |
var self = UT3BOT; | |
})(); | |
</script> | |
<script> | |
// JS version of testbot.py - not very smart. | |
// does nothing until it sees another bot or gets shot. | |
(function() { | |
MYBOT = { | |
start: function() { | |
UT3BOT.start("192.168.0.24", "Madman", self.onmessage ); | |
}, | |
send: UT3BOT.send_msg, | |
onmessage: function(msg) { | |
handler = self.map[msg[0]] | |
if (handler) handler(msg); | |
}, | |
nav: function(msg) { | |
self.send('RUNTO','0.0','0.0',msg[2]); | |
}, | |
seen: function(msg) { | |
self.send('FIRE', msg[1], 'False'); | |
self.send('RUNTO', '0.0', '0.0', msg[6], msg[1]); | |
}, | |
damaged: function(msg) { | |
self.send('FIRE', msg[1], 'False'); | |
self.send('STRAFETO','0.0','0.0', msg[1], msg[2]); | |
} | |
} | |
var self = MYBOT; | |
self.map = { | |
'NAV': self.nav, | |
'PICKUP': self.nav, | |
'SEENPLAYER': self.seen, | |
'HEARDNOISE': self.damaged, | |
'BUMPED': self.damaged, | |
'DAMAGED': self.damaged, | |
} | |
})(); | |
$(document).ready(function(){ | |
MYBOT.start(); | |
}); | |
</script> | |
</head> | |
<body> | |
<div id="debug"></div> | |
<div id="msg"></div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'json' # gem install json | |
require 'em-websocket' # gem install em-websocket | |
require 'em-proxy/backend' # gem install em-proxy | |
DEBUG = true # make the proxy verbose, and probably a bit slower. | |
# An EM thang that proxies a web socket connection to a UT3Bot backend. | |
# Server Side | |
# ruby unreal.rb | |
# | |
# Expects that the ws:// url used to make the connection includes the hostname of the UT3Bot server in | |
# the path part. | |
# Example: ws://localhost:8080/192.168.3.155 | |
# | |
# sends UT3Bot messages back through the web socket as a JSON array string. | |
# | |
# See the example html file for JS side | |
# | |
# Limitations/TODO | |
# The JS code as presented in the gist is a semi-functional, mentally challenged bot. | |
# - tested only on a Mac in Chrome 5.0.342.9 | |
# - can't reconnect to the UT2Bot server if the backend connection is lost | |
# | |
# Kudos to @igrigorik (http://github.com/igrigorik) for the rockin em-websocket and | |
# em-proxy gems. And for a blog that is overflowing with awesome (http://www.igvita.com). | |
class UnrealBackend | |
def initialize(ws, options = {}) | |
@ws = ws | |
@debug = options[:debug] || true | |
@defer = EM::DefaultDeferrable.new | |
@leftover = '' | |
end | |
def start | |
if self.host.size == 0 | |
@ws.send("error: no unreal host found in the websocket url path. This connection is toast.") | |
@defer.callback {@ws.close_connection_after_writing} | |
else | |
debug :onopen, "connecting to Unreal" | |
@server = EventMachine.connect(self.host, 4530, EventMachine::ProxyServer::Backend, false) do |c| | |
c.name = :unreal | |
c.plexer = self | |
end | |
end | |
end | |
def stop | |
@server.close_connection if @server | |
end | |
def send(data) | |
@server.send data | |
end | |
def host | |
return @host if @host | |
@host = @ws.request['Path'] | |
@host.slice! 0 | |
debug :host, "Host = #{@host.inspect}" | |
@host | |
end | |
def relay_from_backend(name, data) | |
raw = @leftover + data | |
@leftover = '' | |
lines = raw.split("\n") | |
lines.each do |msg| | |
cleaned = clean_message(msg) | |
@leftover = cleaned and return unless cleaned.is_a? Array | |
debug :ws_sending, cleaned | |
@ws.send cleaned.to_json | |
end | |
end | |
# called when backend unreal connection is established | |
def connected(*args) | |
# do nothing | |
end | |
# called when backend unreal connection closes | |
def unbind_backend(*args) | |
@ws.send("unreal connection lost".to_json) | |
@defer.callback {ws.close_connection_after_writing} | |
end | |
def clean_message(data) | |
msg = data.strip | |
if msg =~/(.+)EOM$/ | |
$1.split('|') | |
else | |
# not a complete message, return it | |
data | |
end | |
end | |
def debug(*data) | |
if @debug | |
require 'pp' | |
pp data | |
puts | |
end | |
end | |
end | |
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => DEBUG) do |ws| | |
@backend = UnrealBackend.new(ws, :debug => DEBUG) | |
ws.onopen { @backend.start } | |
ws.onmessage { |msg| @backend.send msg } | |
ws.onclose { @backend.stop } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment