-
-
Save zeeZ/1fbad280c600d56f41be to your computer and use it in GitHub Desktop.
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
import threading | |
import time | |
import mmap | |
import urllib2 | |
import math | |
import ctypes | |
try: import simplejson as json | |
except ImportError: import json | |
# http://www.tornadoweb.org/en/stable/ | |
import tornado.httpserver | |
import tornado.websocket | |
import tornado.ioloop | |
import tornado.web | |
_MULTIPLIER = 39.3701 | |
_MAP_INFO_URL = "https://api.guildwars2.com/v1/maps.json?map_id=%d" | |
_NOTIFIER = None | |
class Link(ctypes.Structure): | |
_fields_ = [ | |
("uiVersion", ctypes.c_uint32), | |
("uiTick", ctypes.c_ulong), | |
("fAvatarPosition", ctypes.c_float * 3), | |
("fAvatarFront", ctypes.c_float * 3), | |
("fAvatarTop", ctypes.c_float * 3), | |
("name", ctypes.c_wchar * 256), | |
("fCameraPosition", ctypes.c_float * 3), | |
("fCameraFront", ctypes.c_float * 3), | |
("fCameraTop", ctypes.c_float * 3), | |
("identity", ctypes.c_wchar * 256), | |
("context_len", ctypes.c_uint32), | |
("context", ctypes.c_uint32 * (256/4)), # is actually 256 bytes of whatever | |
("description", ctypes.c_wchar * 2048) | |
] | |
def Unpack(ctype, buf): | |
cstring = ctypes.create_string_buffer(buf) | |
ctype_instance = ctypes.cast(ctypes.pointer(cstring), ctypes.POINTER(ctype)).contents | |
return ctype_instance | |
def continent_coords(continent_rect, map_rect, point): | |
return ( | |
( point[0]-map_rect[0][0])/(map_rect[1][0]-map_rect[0][0])*(continent_rect[1][0]-continent_rect[0][0])+continent_rect[0][0], | |
(-point[1]-map_rect[0][1])/(map_rect[1][1]-map_rect[0][1])*(continent_rect[1][1]-continent_rect[0][1])+continent_rect[0][1] | |
) | |
class Notifier(threading.Thread): | |
def __init__(self): | |
threading.Thread.__init__(self) | |
self.clients = set() | |
self.running = True | |
def register(self, client): | |
self.clients.add(client) | |
def unregister(self, client): | |
self.clients.remove(client) | |
def run(self, ): | |
current_map = 0 | |
current_map_data = None | |
memfile = mmap.mmap(0, ctypes.sizeof(Link), "MumbleLink") | |
while self.running: | |
memfile.seek(0) | |
data = memfile.read(ctypes.sizeof(Link)) | |
result = Unpack(Link, data) | |
if result.context[7] != current_map: | |
# Map change | |
current_map = result.context[7] | |
fp = urllib2.urlopen(_MAP_INFO_URL % current_map) | |
current_map_data = json.load(fp)["maps"][str(current_map)] | |
fp.close() | |
data = { | |
"name": result.identity, | |
"map": result.context[7], | |
"face": -(math.atan2(result.fAvatarFront[2],result.fAvatarFront[0])*180/math.pi)%360 | |
} | |
if current_map_data: | |
data.update({"position": continent_coords(current_map_data["continent_rect"], current_map_data["map_rect"], (result.fAvatarPosition[0]*_MULTIPLIER, result.fAvatarPosition[2]*_MULTIPLIER))}) | |
output = json.dumps(data) | |
for client in self.clients: | |
try: | |
client.write_message(output) | |
except Exception, e: | |
# Look, effort! | |
print e | |
time.sleep(0.1) | |
class WSHandler(tornado.websocket.WebSocketHandler): | |
def open(self): | |
# New connection | |
_NOTIFIER.register(self) | |
def on_close(self): | |
# Connection closed | |
_NOTIFIER.unregister(self) | |
application = tornado.web.Application([ | |
(r'/ws', WSHandler), | |
]) | |
if __name__ == "__main__": | |
_NOTIFIER = Notifier() | |
_NOTIFIER.start() | |
http_server = tornado.httpserver.HTTPServer(application) | |
http_server.listen(8888) | |
tornado.ioloop.IOLoop.instance().start() | |
_NOTIFIER.running = False # As if |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Web Socket Map Thing</title> | |
<meta charset="utf-8" /> | |
<script src="http://code.jquery.com/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.5.1/leaflet.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.5.1/leaflet.css" /> | |
<style type="text/css"> | |
body { | |
text-align: center; | |
min-width: 500px; | |
} | |
.leaflet-container { | |
background: #000; | |
} | |
#map { | |
position: absolute; | |
top: 50px; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
} | |
#message { | |
position: absolute; | |
top: 0; | |
right: 0; | |
left: 0; | |
height: 50px; | |
display: block; | |
} | |
</style> | |
<script> | |
var map; | |
var supermarker; | |
function unproject(coord) { | |
return map.unproject(coord, map.getMaxZoom()); | |
} | |
$(function () { | |
"use strict"; | |
var southWest, northEast; | |
map = L.map("map", { | |
minZoom: 0, | |
maxZoom: 7, | |
crs: L.CRS.Simple | |
}).setView([0, 0], 0); | |
southWest = unproject([0, 32768]); | |
northEast = unproject([32768, 0]); | |
map.setMaxBounds(new L.LatLngBounds(southWest, northEast)); | |
L.tileLayer("https://tiles.guildwars2.com/1/1/{z}/{x}/{y}.jpg", { | |
minZoom: 0, | |
maxZoom: 7, | |
continuousWorld: true | |
}).addTo(map); | |
var posIcon = L.divIcon({ | |
iconSize: [64, 64], | |
iconAnchor: [32, 32], | |
className: 'fancyPlayerPos', | |
html: '<img src="icons/player_position.png">' | |
}); | |
supermarker = L.marker(unproject([0, 0]), { | |
icon: posIcon | |
}).addTo(map); | |
$.getJSON("https://api.guildwars2.com/v1/map_floor.json?continent_id=1&floor=1", function (data) { | |
var region, gameMap, i, il, poi; | |
for (region in data.regions) { | |
region = data.regions[region]; | |
for (gameMap in region.maps) { | |
gameMap = region.maps[gameMap]; | |
for (i = 0, il = gameMap.points_of_interest.length; i < il; i++) { | |
poi = gameMap.points_of_interest[i]; | |
if (poi.type != "waypoint") { | |
continue; | |
} | |
var waypoint = L.icon({ | |
iconUrl: 'icons/waypoint.png', | |
iconSize: [20, 20], | |
iconAnchor: [10, 10], | |
}); | |
L.marker(unproject(poi.coord), { | |
icon: waypoint, | |
title: poi.name | |
}).addTo(map); | |
} | |
} | |
} | |
}); | |
}); | |
$(document).ready(function () { | |
var ws; | |
var host = "localhost"; | |
var port = "8888"; | |
var uri = "/ws"; | |
ws = new WebSocket("ws://" + host + ":" + port + uri); | |
ws.onmessage = function (evt) { | |
$('#message').text(evt.data); | |
var json = $.parseJSON(evt.data); | |
if (json.position) { | |
supermarker.setLatLng(unproject(json.position)); | |
supermarker._icon.title = json.name; | |
supermarker.update(); | |
$('.fancyPlayerPos img').css({ | |
transform: 'scale(' + 1 / (map.getMaxZoom() - map.getZoom() + 1) + ',' + 1 / (map.getMaxZoom() - map.getZoom() + 1) + ') rotate(' + json.face + 'deg)' | |
}); | |
} | |
}; | |
ws.onclose = function (evt) { | |
console.log("Connection close"); | |
}; | |
ws.onopen = function (evt) { | |
console.log("Connection open"); | |
}; | |
}); | |
</script> | |
</head> | |
<body> | |
<div id="message"></div> | |
<div id="map"></div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment