Created
June 3, 2016 15:49
-
-
Save tylerreisinger/9665db14e60c726d5d3b579497e8b8ed 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 httplib | |
import time | |
import xml.dom.minidom as xmlparser | |
import threading | |
import Queue | |
from amfast.decoder import Decoder | |
from amfast.encoder import Encoder | |
import json | |
from Glitch.Events.Event import PacketEvent, LoginCompleteEvent, ShutdownEvent, ReloginStartEvent, ReloginEndEvent | |
from Glitch.Events.Crafting import CraftingStartEvent, CraftingBeginEvent, CraftingEndEvent, RecipeRequestEvent | |
from Glitch.Events.Item import VerbQueryEvent, VerbActionEvent, ItemAddedEvent, ItemDeletedEvent, ItemCountChangedEvent, TeleportationScriptReadEvent, NoteReadEvent | |
from Glitch.Events.Messaging import RoomMessageEvent, LocalMessageEvent | |
from Glitch.Events.WorldEvents import MoveEndEvent, MoveStartEvent | |
from Glitch.Client.Player import LocalPlayer | |
from Glitch.Client.Inventory import CreateItem | |
from Glitch.Client.Messaging import ChatManager | |
from Glitch.Client.World import ActiveLocation | |
import Glitch.Events.Item | |
class LoginHelper(object): | |
def __init__(self, pcId): | |
self._pcId = pcId | |
def GetLoginInfo(self): | |
connection = httplib.HTTPConnection('www.glitch.com') | |
connection.request('GET', '/local.php?p=' + self._pcId + '&cb=' + str(int(time.time() * 1000))) | |
response = connection.getresponse() | |
if response.status == 200: | |
body = response.read() | |
print(body) | |
bodyDOM = xmlparser.parseString(body) | |
token = bodyDOM.getElementsByTagName('token')[0].firstChild.nodeValue | |
host = bodyDOM.getElementsByTagName('host')[0].firstChild.nodeValue | |
return (token, host) | |
else: | |
print("Request for login token failed with code " + str(response.status)) | |
return None | |
class PacketLog(object): | |
def __init__(self, fileName): | |
self._logFile = open(fileName, 'w') | |
def PacketReceived(self, packet): | |
self._logFile.write("Recevied\n" + json.dumps(packet, indent = 3)) | |
self._logFile.flush() | |
def PacketSent(self, packet): | |
self._logFile.write("Sent\n" + json.dumps(packet, indent = 3)) | |
self._logFile.flush() | |
class GlitchClientBase(object): | |
def __init__(self, connectionFactory, pcId, log = None): | |
self._token = None | |
self._connectionFactory = connectionFactory | |
self._connection = connectionFactory.CreateConnection() | |
self._messageId = 1 | |
self._serverAddress = None | |
self._eventQueue = Queue.Queue() | |
self._eventThread = threading.Thread(target = self._EventThreadFunc) | |
self._pcId = pcId | |
self._eventUserData = {} | |
self._packetHandlers = {} | |
self._log = PacketLog('/home/tyler/GlitchPacketLogs/ClientLog') | |
self._shutdownEvent = threading.Event() | |
self._pingTimer = threading.Timer(10.0, self._DoPing) | |
self._localPlayer = None | |
self._activeLocation = None | |
self._chatManager = None | |
self._reloginPreviousLocation = None | |
self._shutdownSelfThread = None | |
self._reloginInformation = None | |
def StartLogin(self): | |
login = LoginHelper(self._pcId) | |
loginInfo = login.GetLoginInfo() | |
self._token = loginInfo[0] | |
self._serverAddress = loginInfo[1][:loginInfo[1].rfind(':')] | |
if loginInfo != None: | |
print("Login token = " + loginInfo[0]) | |
else: | |
print("Login Failed!") | |
return | |
self._connection.Connect(self._serverAddress) | |
self._connection.Start(self._ConnectionReadyInitial) | |
def SendPacket(self, typeName, parameters, msgId = True, userData = None, callbackFn = None, callbackArgs = []): | |
packetObj = {'type' : typeName} | |
returnId = None | |
if msgId == True: | |
returnId = self._messageId | |
packetObj['msg_id'] = self._messageId | |
self._messageId += 1 | |
userDataObj = {} | |
hasUserData = False | |
if userData: | |
userDataObj[u'user_data'] = userData | |
hasUserData = True | |
if callbackFn: | |
userDataObj[u'callback_fn'] = callbackFn | |
userDataObj[u'callback_args'] = callbackArgs | |
hasUserData = True | |
if hasUserData: | |
self._eventUserData[returnId] = userDataObj | |
packetObj.update(parameters) | |
self._OnPacketSend(packetObj) | |
encoder = Encoder(amf3 = True) | |
packet = encoder.encode(packetObj) | |
self._connection.Send(packet) | |
return returnId | |
def WaitForCompletion(self, timeout = None): | |
self._shutdownEvent.wait(timeout) | |
def QueueEvent(self, event): | |
self._eventQueue.put(event) | |
@property | |
def LocalPlayer(self): | |
return self._localPlayer | |
@property | |
def ActiveLocation(self): | |
return self._activeLocation | |
def Logout(self): | |
self.SendPacket('logout', {}) | |
def Shutdown(self): | |
self._pingTimer.cancel() | |
self._connection.Shutdown(True) | |
self._connection.Stop() | |
self._connection.Dispatcher.Stop() | |
self.QueueEvent(ShutdownEvent()) | |
self._connection.Wait() | |
self._connection.Dispatcher.Wait() | |
self._eventThread.join() | |
print("Shutdown complete!") | |
def ShutdownSelf(self, callback = None, callbackArgs = ()): | |
self._pingTimer.cancel() | |
self._connection.Shutdown(True) | |
self._connection.Stop() | |
self._connection.Dispatcher.Stop() | |
self.QueueEvent(ShutdownEvent()) | |
self._connection.Wait() | |
self._connection.Dispatcher.Wait() | |
self._shutdownSelfThread = threading.Thread(target = self._ShutdownSelfThread, args = (callback, callbackArgs)) | |
self._shutdownSelfThread.start() | |
def Stop(self): | |
self.Shutdown() | |
self._activeLocation = None | |
self._chatManager = None | |
self._localPlayer = None | |
self._shutdownEvent.set() | |
def StopSelf(self): | |
self.ShutdownSelf(self._StopSelf) | |
def _ShutdownSelfThread(self, callback, callbackArgs): | |
self._eventThread.join() | |
print("Shutdown complete!") | |
if callback: | |
callback(*callbackArgs) | |
def _StopSelf(self): | |
self._activeLocation = None | |
self._chatManager = None | |
self._localPlayer = None | |
self._shutdownEvent.set() | |
def _ConnectionReadyInitial(self): | |
self._connection.Dispatcher.Start(self) | |
self._eventThread.start() | |
self.SendPacket('login_start', {'token' : self._token}) | |
def _ConnectionReadyRelog(self, token, reloginType, sendMoveEvent): | |
self._connection.Dispatcher.Start(self) | |
self.SendPacket('relogin_start', {'token': token, 'relogin_type': reloginType}, callbackFn = self._ReloginStart, callbackArgs = (token, reloginType, sendMoveEvent)) | |
def _ReloginOnDisconnect(self, serverAddress, token, reloginType, previousLocation = None, sendMoveEvent = False): | |
self._reloginInformation = (serverAddress, token, reloginType, previousLocation, sendMoveEvent) | |
def _StartRelogin(self, serverAddress, token, reloginType, previousLocation = None, sendMoveEvent = False): | |
self._reloginPreviousLocation = previousLocation | |
self.QueueEvent(ReloginStartEvent()) | |
self.ShutdownSelf(self._FinishRelogin, (serverAddress, token, reloginType, sendMoveEvent)) | |
def _FinishRelogin(self, serverAddress, token, reloginType, sendMoveEvent): | |
self._activeLocation = None | |
self._eventQueue = Queue.Queue() | |
self._eventUserData = {} | |
self._connection = self._connectionFactory.CreateConnection() | |
self._connection.Connect(serverAddress) | |
self._eventThread = threading.Thread(target = self._EventThreadFunc) | |
self._eventThread.start() | |
self._connection.Start(self._ConnectionReadyRelog, (token, reloginType, sendMoveEvent)) | |
def _ReloginStart(self, packet, token, reloginType, sendMoveEvent): | |
if packet.packetObject[u'success'] == False: | |
self.StopSelf() | |
print("FATAL: Relogin start returned failure! Aborting.") | |
else: | |
self._activeLocation = ActiveLocation(packet.packetObject[u'location']) | |
self._DoPing() | |
if sendMoveEvent: | |
self.QueueEvent(MoveStartEvent(packet.packetObject, self._reloginPreviousLocation)) | |
self.SendPacket('relogin_end', {'relogin_type': reloginType}, callbackFn = self._ReloginEnd, callbackArgs = (token, reloginType, sendMoveEvent)) | |
def _ReloginEnd(self, packet, token, reloginType, sendMoveEvent): | |
if packet.packetObject[u'success'] == False: | |
self.StopSelf() | |
print("FATAL: Relogin end returned failure! Aborting.") | |
else: | |
self._activeLocation._MoveEnd(packet.packetObject[u'location']) | |
self.QueueEvent(ReloginEndEvent()) | |
if sendMoveEvent: | |
self.QueueEvent(MoveEndEvent(packet.packetObject, self._reloginPreviousLocation)) | |
def _FullPacketReceived(self, packet): | |
decoder = Decoder(amf3 = True) | |
packetObj = decoder.decode(packet) | |
self._OnPacketReceived(packetObj) | |
self._eventQueue.put(PacketEvent(packetObj)) | |
def _OnPacketReceived(self, packet): | |
if 1==1 or self._log and packet[u'type'] != 'location_item_moves' and packet[u'type'] != 'login_start' and packet[u'type'] != 'login_end' and packet[u'type'] != 'location_event' and packet[u'type'] != "item_state": | |
pass | |
#self._log.PacketReceived(packet) | |
def _OnPacketSend(self, packet): | |
if 1==1 or self._log: | |
pass | |
#self._log.PacketSent(packet) | |
def _LocationChange(self, packet): | |
self._activeLocation = ActiveLocation(packet) | |
def _EventThreadFunc(self): | |
while True: | |
try: | |
event = self._eventQueue.get() | |
if isinstance(event, PacketEvent): | |
if event.Type == 'login_start': | |
if event.packetObject[u'success'] == True: | |
self._localPlayer = LocalPlayer(self._pcId, event.packetObject) | |
self._chatManager = ChatManager(event.packetObject[u'global_chat_group'], event.packetObject[u'live_help_group']) | |
self._DoPing() | |
print("Login Success!") | |
self._activeLocation = ActiveLocation(event.packetObject[u'location']) | |
self.SendPacket('login_end', {}) | |
elif event.Type == 'login_end' and event.packetObject[u'success'] == True: | |
self._activeLocation._MoveEnd(event.packetObject[u'location']) | |
elif event.Type == 'pc_move_xy': | |
self._HandleMoveXY(event.packetObject) | |
elif event.Type == 'pc_signpost_move' or event.Type == 'pc_teleport_move' or event.Type == 'pc_follow_move' or event.Type == 'pc_door_move': | |
self.ActiveLocation._HandlePcMove(self, event.packetObject) | |
elif event.Type == 'buff_remove': | |
self.LocalPlayer._RemoveBuff(self, event.packetObject[u'tsid']) | |
elif event.Type == 'buff_start': | |
self.LocalPlayer._AddBuff(self, event.packetObject) | |
elif event.Type == 'buff_update': | |
self.LocalPlayer._UpdateBuff(self, event.packetObject) | |
elif event.Type == 'server_message' and event.packetObject[u'action'] == 'CLOSE' and event.packetObject[u'msg'] == 'CONNECT_TO_ANOTHER_SERVER': | |
if self._reloginInformation: | |
self._StartRelogin(*self._reloginInformation) | |
self._reloginInformation = None | |
else: | |
print('Server closed the connection!') | |
self._StopSelf() | |
if u'changes' in event.packetObject: | |
self._HandleChangeObject(event.packetObject[u'changes']) | |
if u'teleportation' in event.packetObject: | |
self._localPlayer._TeleportUpdate(event.packetObject) | |
newEvent = self._CreateEvents(event) | |
if u'msg_id' in event.packetObject: | |
if event.packetObject[u'msg_id'] in self._eventUserData: | |
userDataObj = self._eventUserData[event.packetObject[u'msg_id']] | |
if 'callback_fn' in userDataObj: | |
userDataObj['callback_fn'](event, *userDataObj[u'callback_args']) | |
del self._eventUserData[event.packetObject[u'msg_id']] | |
if newEvent: | |
self.QueueEvent(newEvent) | |
elif isinstance(event, ShutdownEvent): | |
print("Shutting down...") | |
self._EventCallback(event) | |
self._eventQueue.task_done() | |
return | |
self._EventCallback(event) | |
except Exception: | |
self.Logout() | |
self.StopSelf() | |
raise | |
self._eventQueue.task_done() | |
def _CreateEvents(self, packetEvent): | |
event = None | |
if packetEvent.Type == 'itemstack_verb_menu': | |
pass#event = VerbQueryEvent(packetEvent.packetObject[u'itemDef']) | |
elif packetEvent.Type == 'itemstack_verb': | |
pass#event = VerbActionEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'making_start': | |
event = CraftingStartEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'make_known_complete': | |
event = CraftingEndEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'login_end' and packetEvent.packetObject[u'success'] == True: | |
event = LoginCompleteEvent() | |
elif packetEvent.Type == 'pc_groups_chat': | |
event = RoomMessageEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'pc_local_chat': | |
event = LocalMessageEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'recipe_request': | |
event = RecipeRequestEvent(packetEvent.packetObject) | |
elif packetEvent.Type == 'teleport_move_start': | |
self._HandleMoveStart(packetEvent.packetObject, 'teleport') | |
elif packetEvent.Type == 'teleportation_script_view': | |
item = None | |
if u'itemstack_tsid' in packetEvent.packetObject: | |
item = self._localPlayer.Inventory.GetItemById(packetEvent.packetObject[u'itemstack_tsid']) | |
event = TeleportationScriptReadEvent(packetEvent.packetObject, item) | |
elif packetEvent.Type == 'note_view': | |
item = None | |
if u'itemstack_tsid' in packetEvent.packetObject: | |
item = self._localPlayer.Inventory.GetItemById(packetEvent.packetObject[u'itemstack_tsid']) | |
event = NoteReadEvent(packetEvent.packetObject, item) | |
if event: | |
if u'msg_id' in packetEvent.packetObject: | |
event._msgId = packetEvent.packetObject[u'msg_id'] | |
if packetEvent.packetObject[u'msg_id'] in self._eventUserData: | |
userDataObj = self._eventUserData[packetEvent.packetObject[u'msg_id']] | |
if 'user_data' in userDataObj: | |
event._userData = userDataObj['user_data'] | |
return event | |
def _EventCallback(self, event): | |
print type(event).__name__ | |
if isinstance(event, PacketEvent): | |
print(event.packetObject[u'type']) | |
if isinstance(event, LoginCompleteEvent): | |
self.Logout() | |
self.StopSelf() | |
#self.LocalPlayer.GoHome(self) | |
#bags = [] | |
#for item in self.LocalPlayer.Inventory: | |
# if item.ParentContainer == None: | |
# bags.append(item) | |
# print(item.Label) | |
#self.Logout() | |
#self.StopSelf() | |
def _DoPing(self): | |
self.SendPacket('ping', {'tsid' : self._pcId}, False) | |
self._pingTimer = threading.Timer(10.0, self._DoPing) | |
self._pingTimer.start() | |
def _HandleMoveXY(self, packetObj): | |
if u'pc' in packetObj: | |
if packetObj[u'pc'][u'tsid'] == self._localPlayer.Id: | |
self._localPlayer._MoveXY(packetObj[u'pc'][u'x'], packetObj[u'pc'][u'y']) | |
else: | |
self._activeLocation._PlayerMoved(packetObj[u'pc'][u'tsid'], packetObj[u'pc'][u'x'], packetObj[u'pc'][u'y']) | |
def _HandleMoveStart(self, packetObj, reloginType): | |
if not u'success' in packetObj or packetObj[u'success'] == True: | |
if u'hostport' in packetObj: | |
self._ReloginOnDisconnect(packetObj[u'hostport'][:packetObj[u'hostport'].find(':')], packetObj[u'token'], reloginType, self._activeLocation, True) | |
else: | |
previousLocation = self._activeLocation | |
self._LocationChange(packetObj[u'location']) | |
event = MoveStartEvent(packetObj, previousLocation) | |
self.QueueEvent(event) | |
self.SendPacket(packetObj[u'type'].replace('start', 'end'), {'from_location_tsid': previousLocation.Id, 'to_location_tsid': packetObj[u'loading_info'][u'to_tsid']}, callbackFn = self._HandleMoveEnd, callbackArgs = (previousLocation,)) | |
event = MoveStartEvent(packetObj, previousLocation) | |
self.QueueEvent(event) | |
else: | |
event = MoveStartEvent(packetObj, self._activeLocation) | |
self.QueueEvent(event) | |
def _HandleMoveEnd(self, packet, previousLocation): | |
if packet.packetObject[u'success'] == True: | |
self._activeLocation._MoveEnd(packet.packetObject[u'location']) | |
event = MoveEndEvent(packet.packetObject, previousLocation) | |
self.QueueEvent(event) | |
else: | |
event = MoveEndEvent(packet.packetObject, previousLocation) | |
self.QueueEvent(event) | |
def _HandleChangeObject(self, obj): | |
if u'stat_values' in obj: | |
self._localPlayer._StatsChange(obj[u'stat_values']) | |
if u'itemstack_values' in obj: | |
if u'pc' in obj[u'itemstack_values']: | |
inventory = self._localPlayer.Inventory | |
for k, v in obj[u'itemstack_values'][u'pc'].iteritems(): | |
item = inventory.GetItemById(k) | |
if item: | |
if v[u'class_tsid'] == 'DELETED': | |
event = ItemDeletedEvent(inventory.GetItemById(k)) | |
inventory._RemoveItem(k) | |
self.QueueEvent(event) | |
else: | |
event = None | |
if v[u'count'] != inventory.GetItemById(k).Count: | |
event = ItemCountChangedEvent(inventory.GetItemById(k), inventory.GetItemById(k).Count) | |
item._UpdateItem(v) | |
if event: | |
self.QueueEvent(event) | |
#print("Updated item " + k) | |
else: | |
item = CreateItem(v, k) | |
inventory._AddItem(item) | |
event = ItemAddedEvent(item) | |
self.QueueEvent(event) | |
#print("New item " + k) | |
elif u'location' in obj[u'itemstack_values']: | |
self._activeLocation._UpdateEntites(obj[u'itemstack_values'][u'location']) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment