-
Notifications
You must be signed in to change notification settings - Fork 2
/
ttr-linux-cli
executable file
·235 lines (196 loc) · 9.04 KB
/
ttr-linux-cli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/usr/bin/python3
import bz2
from getpass import getpass
import hashlib
import os
import platform
import requests
import sys
import subprocess
import stat
import time
PATCH_MANIFEST_BASE = 'https://cdn.toontownrewritten.com'
CONTENT_MIRROR = 'https://download.toontownrewritten.com/patches/'
LOGIN_API = 'https://www.toontownrewritten.com/api/login?format=json'
RELEASE_NOTES_API = 'https://www.toontownrewritten.com/api/releasenotes?format=json'
STATUS_API = 'https://www.toontownrewritten.com/api/status?format=json'
HEADERS = {'Content-Type': 'application/x-www-form-urlencoded'}
OPTION_ENABLE_LOGGING = False
OPTION_USERNAME = None
OPTION_PASSWORD = None
def display_help():
print("usage: {} [-l|--enable-logging] [-u|--username username] [-p|--password password]".format(sys.argv[0]))
print("\t-l, --enable-logging Allows the game to print log output to the console.")
print("")
print("\t-u, --username Your account username. Requires -p/--password to be used.")
print("\t-p, --password Your account password. Requires -u/--username to be used.")
print("")
print("\t-h, --help Displays this help message and exits.")
def get_json_response_from_get_request(api_url, data=None, headers=HEADERS):
try:
response = requests.get(api_url, data=data, headers=headers)
if response.status_code != 200:
print("error: Could not connect to Toontown Rewritten's API. (error {})".format(response.status_code))
os._exit(1)
except:
print("error: Could not connect to Toontown Rewritten's API.")
os._exit(1)
return response.json()
def get_json_response_from_post_request(api_url, data=None, headers=HEADERS):
try:
response = requests.post(api_url, data=data, headers=headers)
if response.status_code != 200:
print("error: Could not connect to Toontown Rewritten's API. (error {})".format(response.status_code))
os._exit(1)
except:
print("error: Could not connect to Toontown Rewritten's API.")
os._exit(1)
return response.json()
def get_latest_game_version():
release_list = get_json_response_from_get_request(RELEASE_NOTES_API)
latest_version = release_list[0]["slug"]
return latest_version
def get_sha1_hash(filename):
BLOCK_SIZE = 65536
hasher = hashlib.sha1()
with open(filename, 'rb') as afile:
buf = afile.read(BLOCK_SIZE)
while len(buf) > 0:
hasher.update(buf)
buf = afile.read(BLOCK_SIZE)
result = hasher.hexdigest()
return result
def update_file(filename, manifest):
if os.path.isfile(filename):
if get_sha1_hash(filename) != manifest[filename]["hash"]:
print("updating {}...".format(filename))
file = requests.get(CONTENT_MIRROR + manifest[filename]["dl"], headers=HEADERS)
with open(filename + '.bz2', 'wb') as zipped:
zipped.write(file.content)
with open(filename, 'wb') as newfile, open(filename + '.bz2', 'rb') as zipped:
newfile.write(bz2.decompress(zipped.read()))
os.remove(filename + ".bz2")
else:
print("downloading {}...".format(filename))
file = requests.get(CONTENT_MIRROR + manifest[filename]["dl"], headers=HEADERS)
with open(filename + '.bz2', 'wb') as zipped:
zipped.write(file.content)
with open(filename, 'wb') as newfile, open(filename + '.bz2', 'rb') as zipped:
newfile.write(bz2.decompress(zipped.read()))
os.remove(filename + ".bz2")
def get_files_for_operating_system(manifest, operating_system):
os_name_dict = {'Windows': 'win64', 'Darwin': 'darwin', 'Linux': 'linux'}
files = []
for filename in manifest:
for system in manifest[filename]["only"]:
if system == os_name_dict[operating_system]:
files.append(filename)
break
return files
def check_for_updates(manifest_path: str):
print("checking for game updates...")
latest_version = get_latest_game_version()
print("latest game version is {}".format(latest_version))
manifest = get_json_response_from_get_request(PATCH_MANIFEST_BASE + manifest_path)
operating_system = platform.system()
files = get_files_for_operating_system(manifest, operating_system)
for file in files:
update_file(file, manifest)
if operating_system == 'Linux':
os.chmod('TTREngine', os.stat('TTREngine').st_mode | stat.S_IEXEC | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
elif operating_system == 'Darwin':
os.chmod('Toontown Rewritten', os.stat('Toontown Rewritten').st_mode | stat.S_IEXEC | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
print("Game is up to date.")
def login():
details = {'username': username, 'password': password}
response = get_json_response_from_post_request(LOGIN_API, details)
if response["success"] == "false":
print("login failed: {}".format(response["banner"]))
os._exit(1)
elif response["success"] == "delayed":
print("game entrance delay detected")
print("the server should have you ready to go in {} seconds.".format(response["eta"]))
delay(response["queueToken"])
elif response["success"] == "partial":
print("enter the ToonGuard code that was just emailed to you.")
guard = input("> ")
auth_details = {'appToken': guard, 'authToken': response['responseToken']}
response = get_json_response_from_post_request(LOGIN_API, auth_details)
if response["success"] == "true":
check_for_updates(response["manifest"])
launch(response["cookie"], response["gameserver"])
elif response["success"] == "delayed":
print("game entrance delay detected")
print("the server should have you ready to go in {} seconds.".format(response["eta"]))
delay(response["queueToken"])
else:
print("login failed: {}".format(response["banner"]))
os._exit(1)
elif response["success"] == "true":
check_for_updates(response["manifest"])
launch(response["cookie"], response["gameserver"])
def launch(cookie, server):
print("you're good to go. launching the game...")
os.environ['TTR_PLAYCOOKIE'] = cookie
os.environ['TTR_GAMESERVER'] = server
if platform.system() == 'Windows':
subprocess.call('TTREngine64.exe', stdout=subprocess.DEVNULL if not OPTION_ENABLE_LOGGING else None,
stderr=subprocess.STDOUT if not OPTION_ENABLE_LOGGING else None)
elif platform.system() == 'Darwin':
subprocess.call('./Toontown Rewritten', stdout=subprocess.DEVNULL if not OPTION_ENABLE_LOGGING else None,
stderr=subprocess.STDOUT if not OPTION_ENABLE_LOGGING else None)
elif platform.system() == 'Linux':
subprocess.call('./TTREngine', stdout=subprocess.DEVNULL if not OPTION_ENABLE_LOGGING else None,
stderr=subprocess.STDOUT if not OPTION_ENABLE_LOGGING else None)
else:
print("{} is unsupported, sorry :(".format(platform.system()))
os._exit(1)
print("game closed, exiting the launcher...")
def delay(token):
details = {'username': username, 'password': password, 'queueToken': token}
delayed = True
while delayed:
time.sleep(2)
print("still trying...")
response = get_json_response_from_post_request(LOGIN_API, details)
if response["success"] == "true":
launch(response["cookie"], response["gameserver"])
delayed = False
else:
print("launch failed: {}".format(response["banner"]))
os._exit(1)
if __name__ == "__main__":
print("ttr-linux-cli by reindeer-cafe, tgsm\n")
# arg parsing
for i in range(len(sys.argv)):
# Displays usage information and exits
if sys.argv[i] == "-h" or sys.argv[i] == "--help":
display_help()
os._exit(0)
elif sys.argv[i] == "-u" or sys.argv[i] == "--username":
try:
OPTION_USERNAME = sys.argv[i + 1]
except IndexError:
print("error: -u/--username was passed, but no actual username was given.\n")
display_help()
os._exit(1)
elif sys.argv[i] == "-p" or sys.argv[i] == "--password":
try:
OPTION_PASSWORD = sys.argv[i + 1]
except IndexError:
print("error: -p/--password was passed, but no actual password was given.\n")
display_help()
os._exit(1)
elif sys.argv[i] == "-l" or sys.argv[i] == "--enable-logging":
OPTION_ENABLE_LOGGING = True
game_status = get_json_response_from_get_request(STATUS_API)
if game_status["open"] != True:
print(game_status["banner"])
os._exit(1)
if not OPTION_USERNAME and not OPTION_PASSWORD:
username = input("username: ")
password = getpass("password: ")
else:
username = OPTION_USERNAME
password = OPTION_PASSWORD
login()