mirror of
https://github.com/zoffline/zwift-offline.git
synced 2026-07-30 23:50:20 -07:00
Update to Zwift 1.0.105233
This commit is contained in:
@@ -17,6 +17,7 @@ There are three ways with which to install and run zoffline depending on your pl
|
||||
To install zoffline on Windows:
|
||||
|
||||
* Download the latest zoffline release from https://github.com/zoffline/zwift-offline/releases
|
||||
* If you want the pace partners, download the source code package and extract the ``pace_partners`` directory to the same folder zoffline is in.
|
||||
* Run the downloaded zoffline.exe
|
||||
* Once run, zoffline will create a ``storage`` directory in the same folder it's in to store your Zwift progress.
|
||||
* Start Zwift with zoffline.exe running (__after completing step 2__ or running __configure_client__ script from https://github.com/zoffline/zwift-offline/releases/tag/zoffline_helper)
|
||||
@@ -93,10 +94,10 @@ zoffline can be installed on the same machine as Zwift or another local machine.
|
||||
<details><summary>Windows Instructions</summary>
|
||||
|
||||
* Install Zwift
|
||||
* If your Zwift version is 1.0.101500, you're all set.
|
||||
* If your Zwift version is 1.0.105233, you're all set.
|
||||
* If Zwift is not installed, install it before installing zoffline.
|
||||
* If your Zwift version is newer than 1.0.101500 and zoffline is running from source: copy ``C:\Program Files (x86)\Zwift\Zwift_ver_cur.xml`` to zoffline's ``cdn/gameassets/Zwift_Updates_Root/`` overwriting the existing file.
|
||||
* If your Zwift version is newer than 1.0.101500 and zoffline is not running from source: wait for zoffline to be updated.
|
||||
* If your Zwift version is newer than 1.0.105233 and zoffline is running from source: copy ``C:\Program Files (x86)\Zwift\Zwift_ver_cur.xml`` to zoffline's ``cdn/gameassets/Zwift_Updates_Root/`` overwriting the existing file.
|
||||
* If your Zwift version is newer than 1.0.105233 and zoffline is not running from source: wait for zoffline to be updated.
|
||||
* __NOTE:__ instead of performing the steps below you can instead just run the __configure_client__ script from https://github.com/zoffline/zwift-offline/releases/tag/zoffline_helper
|
||||
* On your Windows machine running Zwift, copy the following files in this repo to a known location:
|
||||
* ``ssl/cert-zwift-com.p12``
|
||||
@@ -122,9 +123,9 @@ to generate your own certificates and do the same.
|
||||
<details><summary>macOS Instructions</summary>
|
||||
|
||||
* Install Zwift
|
||||
* If your Zwift version is 1.0.101500, you're all set.
|
||||
* If your Zwift version is 1.0.105233, you're all set.
|
||||
* If Zwift is not installed, install it before installing zoffline.
|
||||
* If your Zwift version is newer than 1.0.101500: copy ``~/Library/Application Support/Zwift/ZwiftMac_ver_cur.xml`` to zoffline's ``cdn/gameassets/Zwift_Updates_Root/`` overwriting the existing file.
|
||||
* If your Zwift version is newer than 1.0.105233: copy ``~/Library/Application Support/Zwift/ZwiftMac_ver_cur.xml`` to zoffline's ``cdn/gameassets/Zwift_Updates_Root/`` overwriting the existing file.
|
||||
* On your Mac machine running Zwift, copy the following files in this repo to a known location:
|
||||
* ``ssl/cert-zwift-com.p12``
|
||||
* ``ssl/cert-zwift-com.pem``
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Zwift
|
||||
version="1.0.101500"
|
||||
sversion="1.28.1 (101500)"
|
||||
gbranch="build/101500-rc-1.28.1.16-1.28.1"
|
||||
gcommit="a08dad09e39cb26d8f279fd66fb9aeae29b7dd5e"
|
||||
version="1.0.105233"
|
||||
sversion="1.29.0 (105233)"
|
||||
gbranch="rc/1.29.0"
|
||||
gcommit="551bf6896edb8cccfa0298a59d321f81eb4e42a8"
|
||||
GAME_URL="https://us-or-rly101.zwift.com"
|
||||
manifest="ZwiftMac_1.0.101500_1fe3bc19_manifest.xml"
|
||||
manifest_checksum="1178012635"
|
||||
ver_cur_checksum="529725630"/>
|
||||
manifest="ZwiftMac_1.0.105233_27ea3fb2_manifest.xml"
|
||||
manifest_checksum="-852792274"
|
||||
ver_cur_checksum="-1509200237"/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Zwift
|
||||
version="1.0.101500"
|
||||
sversion="1.28.1 (101500)"
|
||||
gbranch="build/101500-rc-1.28.1.16-1.28.1"
|
||||
gcommit="a08dad09e39cb26d8f279fd66fb9aeae29b7dd5e"
|
||||
version="1.0.105233"
|
||||
sversion="1.29.0 (105233)"
|
||||
gbranch="rc/1.29.0"
|
||||
gcommit="551bf6896edb8cccfa0298a59d321f81eb4e42a8"
|
||||
GAME_URL="https://us-or-rly101.zwift.com"
|
||||
manifest="Zwift_1.0.101500_1fe3bc19_manifest.xml"
|
||||
manifest_checksum="1425423777"
|
||||
ver_cur_checksum="-15275599"/>
|
||||
manifest="Zwift_1.0.105233_27ea3fb2_manifest.xml"
|
||||
manifest_checksum="-1173342509"
|
||||
ver_cur_checksum="-670280471"/>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+110
-8
@@ -244,11 +244,30 @@
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_28_avoid_aborting_current_timing_sector_when_minimizing",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_27_add_lineup_to_leave_current_event",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_integrate_road_and_bike_type_into_braking_performance",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_use_relay_spline_time_in_meetup_position_late_joiner",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_ignore_private_meetups_in_group_event_processing_logic",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_upload_route_results_v2",
|
||||
"value": true,
|
||||
@@ -260,6 +279,7 @@
|
||||
},
|
||||
{
|
||||
"name": "game_1_25_extended_pedal_asssit",
|
||||
"value": true,
|
||||
"values": {
|
||||
"fields": {
|
||||
"initialDropInTime": {
|
||||
@@ -288,7 +308,7 @@
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_28_ble_middleware",
|
||||
"name": "game_1_29_ble_middleware",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
@@ -298,6 +318,7 @@
|
||||
},
|
||||
{
|
||||
"name": "game_1_27_atv_remote_gestures",
|
||||
"value": true,
|
||||
"values": {
|
||||
"fields": {
|
||||
"ATVPopInitDist": {
|
||||
@@ -354,7 +375,51 @@
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_28_event_core_feed_service",
|
||||
"name": "game_1_29_get_started_workouts",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_28_ftms_road_grade_lookahead_time_exposed",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_ble_cycling_speed_pairing_opt",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_new_local_trainer_profile",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_fix_stuck_zpower",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_event_carousel",
|
||||
"value": true,
|
||||
"values": {
|
||||
"fields": {
|
||||
"NUM_CARDS": {
|
||||
"varintValue": "6"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_client_recommendations",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_level_60_cap_raise",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_logout_for_windows_and_mac",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
@@ -372,19 +437,51 @@
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_26_watchdog_timer",
|
||||
"name": "game_1_28_event_core_feed_service",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_event_query_interval",
|
||||
"value": true,
|
||||
"values": {
|
||||
"fields": {
|
||||
"timeoutInterval": {
|
||||
"numberValue": 60.0
|
||||
},
|
||||
"abortEnabled": {
|
||||
"boolValue": false
|
||||
"GROUP_EVENT_QUERY_RATE_SECONDS": {
|
||||
"numberValue": 120.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_set_thread_names",
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_28_watchdog_timer",
|
||||
"values": {
|
||||
"fields": {
|
||||
"abortEnabled": {
|
||||
"boolValue": false
|
||||
},
|
||||
"renderTimeoutInterval": {
|
||||
"numberValue": 30.0
|
||||
},
|
||||
"mainTimeoutInterval": {
|
||||
"numberValue": 30.0
|
||||
},
|
||||
"reportTimeoutsOnLoad": {
|
||||
"boolValue": false
|
||||
},
|
||||
"loadingTimeoutInterval": {
|
||||
"numberValue": 60.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "game_1_29_hand_cycle",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_17_noesis_enabled",
|
||||
"value": true,
|
||||
@@ -401,6 +498,11 @@
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "workout_training_v2_release",
|
||||
"value": true,
|
||||
"values": {}
|
||||
},
|
||||
{
|
||||
"name": "game_1_23_workout_auto_pause",
|
||||
"values": {
|
||||
|
||||
+58
-89
@@ -800,18 +800,15 @@ def upload(username):
|
||||
@app.route("/download/profile.bin", methods=["GET"])
|
||||
@login_required
|
||||
def download_profile():
|
||||
player_id = current_user.player_id
|
||||
profile_dir = os.path.join(STORAGE_DIR, str(player_id))
|
||||
profile_file = os.path.join(profile_dir, 'profile.bin')
|
||||
profile_file = os.path.join(STORAGE_DIR, str(current_user.player_id), 'profile.bin')
|
||||
if os.path.isfile(profile_file):
|
||||
return send_file(profile_file, attachment_filename='profile.bin')
|
||||
return send_file(profile_file)
|
||||
|
||||
@app.route("/download/<int:player_id>/avatarLarge.jpg", methods=["GET"])
|
||||
def download_avatarLarge(player_id):
|
||||
profile_dir = os.path.join(STORAGE_DIR, str(player_id))
|
||||
profile_file = os.path.join(profile_dir, 'avatarLarge.jpg')
|
||||
profile_file = os.path.join(STORAGE_DIR, str(player_id), 'avatarLarge.jpg')
|
||||
if os.path.isfile(profile_file):
|
||||
return send_file(profile_file, mimetype='image/jpeg', attachment_filename='avatarLarge.jpg')
|
||||
return send_file(profile_file, mimetype='image/jpeg')
|
||||
else:
|
||||
return '', 404
|
||||
|
||||
@@ -1708,10 +1705,10 @@ def player_playbacks_player_playback():
|
||||
f.write(stream)
|
||||
return '', 200
|
||||
|
||||
@app.route('/player-playbacks/player/me/playbacks/<segment_id>/pr', methods=['GET'])
|
||||
@app.route('/player-playbacks/player/me/playbacks/<segment_id>/<option>', methods=['GET'])
|
||||
@jwt_to_session_cookie
|
||||
@login_required
|
||||
def player_playbacks_player_me_playbacks_pr(segment_id):
|
||||
def player_playbacks_player_me_playbacks(segment_id, option):
|
||||
segment_id = int(segment_id) & 0xffffffffffffffff
|
||||
after = request.args.get('after')
|
||||
before = request.args.get('before')
|
||||
@@ -1720,7 +1717,10 @@ def player_playbacks_player_me_playbacks_pr(segment_id):
|
||||
query += " AND world_time > '%s'" % after
|
||||
if before:
|
||||
query += " AND world_time < '%s'" % before
|
||||
query += " ORDER BY time"
|
||||
if option == 'pr':
|
||||
query += " ORDER BY time"
|
||||
elif option == 'latest':
|
||||
query += " ORDER BY world_time DESC"
|
||||
row = db.session.execute(sqlalchemy.text(query)).first()
|
||||
if not row:
|
||||
return '', 200
|
||||
@@ -2716,73 +2716,50 @@ def relay_worlds_attributes(server_realm=udp_node_msgs_pb2.ZofflineConstants.Rea
|
||||
return '', 201
|
||||
|
||||
|
||||
def add_segment_results(segment_id, player_id, only_best, from_date, to_date, results):
|
||||
where_stmt = ("WHERE segment_id = '%s'" % segment_id)
|
||||
rows = None
|
||||
if player_id:
|
||||
where_stmt += (" AND player_id = '%s'" % player_id)
|
||||
if from_date:
|
||||
where_stmt += (" AND strftime('%s', finish_time_str) > strftime('%s', '%s')" % ('%s', '%s', from_date))
|
||||
if to_date:
|
||||
where_stmt += (" AND strftime('%s', finish_time_str) < strftime('%s', '%s')" % ('%s', '%s', to_date))
|
||||
if only_best:
|
||||
#Only include results from max 1 hour ago
|
||||
where_stmt += (" AND world_time > '%s'" % (world_time()-(60*60*1000)))
|
||||
rows = db.session.execute(sqlalchemy.text("""SELECT s1.* FROM segment_result s1
|
||||
JOIN (SELECT s.player_id, MIN(Cast(s.elapsed_ms AS INTEGER)) AS min_time
|
||||
FROM segment_result s %s GROUP BY s.player_id) s2 ON s2.player_id = s1.player_id AND s2.min_time = CAST(s1.elapsed_ms AS INTEGER)
|
||||
GROUP BY s1.player_id, s1.elapsed_ms
|
||||
ORDER BY CAST(s1.elapsed_ms AS INTEGER)
|
||||
LIMIT 1000""" % where_stmt))
|
||||
else:
|
||||
rows = db.session.execute(sqlalchemy.text("SELECT * FROM segment_result %s" % where_stmt))
|
||||
for row in rows:
|
||||
result = results.segment_results.add()
|
||||
row_to_protobuf(row, result, ['f3', 'f4', 'segment_id', 'event_subgroup_id', 'finish_time_str', 'f14', 'f17', 'f18'])
|
||||
@app.route('/api/segment-results', methods=['POST'])
|
||||
@jwt_to_session_cookie
|
||||
@login_required
|
||||
def api_segment_results():
|
||||
if not request.stream:
|
||||
return '', 400
|
||||
data = request.stream.read()
|
||||
result = segment_result_pb2.SegmentResult()
|
||||
result.ParseFromString(data)
|
||||
if result.segment_id == 1:
|
||||
return '', 400
|
||||
result.id = get_id('segment_result')
|
||||
result.world_time = world_time()
|
||||
result.finish_time_str = get_utc_date_time().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
result.f20 = 0
|
||||
insert_protobuf_into_db('segment_result', result)
|
||||
|
||||
def handle_segment_results(request):
|
||||
if request.method == 'POST':
|
||||
if not request.stream:
|
||||
return '', 400
|
||||
data = request.stream.read()
|
||||
result = segment_result_pb2.SegmentResult()
|
||||
result.ParseFromString(data)
|
||||
if result.segment_id == 1:
|
||||
return '', 400
|
||||
result.id = get_id('segment_result')
|
||||
result.world_time = world_time()
|
||||
result.finish_time_str = get_utc_date_time().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
result.f20 = 0
|
||||
insert_protobuf_into_db('segment_result', result)
|
||||
# Previously done in /relay/worlds/attributes
|
||||
player_update = udp_node_msgs_pb2.WorldAttribute()
|
||||
player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID
|
||||
player_update.wa_type = udp_node_msgs_pb2.WA_TYPE.WAT_SR
|
||||
player_update.payload = data
|
||||
player_update.world_time_born = world_time()
|
||||
player_update.world_time_expire = world_time() + 60000
|
||||
player_update.timestamp = int(get_utc_time() * 1000000)
|
||||
sending_player_id = result.player_id
|
||||
if sending_player_id in online:
|
||||
sending_player = online[sending_player_id]
|
||||
for receiving_player_id in online.keys():
|
||||
if receiving_player_id != sending_player_id:
|
||||
receiving_player = online[receiving_player_id]
|
||||
if get_course(sending_player) == get_course(receiving_player) or receiving_player.watchingRiderId == sending_player_id:
|
||||
enqueue_player_update(receiving_player_id, player_update.SerializeToString())
|
||||
|
||||
# Previously done in /relay/worlds/attributes
|
||||
player_update = udp_node_msgs_pb2.WorldAttribute()
|
||||
player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID
|
||||
player_update.wa_type = udp_node_msgs_pb2.WA_TYPE.WAT_SR
|
||||
player_update.payload = data
|
||||
player_update.world_time_born = world_time()
|
||||
player_update.world_time_expire = world_time() + 60000
|
||||
player_update.timestamp = int(get_utc_time() * 1000000)
|
||||
sending_player_id = result.player_id
|
||||
if sending_player_id in online:
|
||||
sending_player = online[sending_player_id]
|
||||
for receiving_player_id in online.keys():
|
||||
if receiving_player_id != sending_player_id:
|
||||
receiving_player = online[receiving_player_id]
|
||||
if get_course(sending_player) == get_course(receiving_player) or receiving_player.watchingRiderId == sending_player_id:
|
||||
enqueue_player_update(receiving_player_id, player_update.SerializeToString())
|
||||
return {"id": result.id}
|
||||
|
||||
return {"id": result.id}
|
||||
|
||||
# request.method == GET
|
||||
# world_id = int(request.args.get('world_id'))
|
||||
player_id = request.args.get('player_id')
|
||||
# full = request.args.get('full') == 'true'
|
||||
# Require segment_id
|
||||
if not request.args.get('segment_id'):
|
||||
@app.route('/api/personal-records/my-records', methods=['GET'])
|
||||
@jwt_to_session_cookie
|
||||
@login_required
|
||||
def api_personal_records_my_records():
|
||||
if not request.args.get('segmentId'):
|
||||
return '', 422
|
||||
segment_id = int(request.args.get('segment_id')) & 0xffffffffffffffff
|
||||
only_best = request.args.get('only-best') == 'true'
|
||||
segment_id = int(request.args.get('segmentId')) & 0xffffffffffffffff
|
||||
from_date = request.args.get('from')
|
||||
to_date = request.args.get('to')
|
||||
|
||||
@@ -2790,27 +2767,19 @@ def handle_segment_results(request):
|
||||
results.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID
|
||||
results.segment_id = segment_id
|
||||
|
||||
add_segment_results(segment_id, player_id, only_best, from_date, to_date, results)
|
||||
where_stmt = ("WHERE segment_id = '%s' AND player_id = '%s'" % (segment_id, current_user.player_id))
|
||||
if from_date:
|
||||
where_stmt += (" AND strftime('%s', finish_time_str) > strftime('%s', '%s')" % ('%s', '%s', from_date))
|
||||
if to_date:
|
||||
where_stmt += (" AND strftime('%s', finish_time_str) < strftime('%s', '%s')" % ('%s', '%s', to_date))
|
||||
rows = db.session.execute(sqlalchemy.text("SELECT * FROM segment_result %s" % where_stmt))
|
||||
for row in rows:
|
||||
result = results.segment_results.add()
|
||||
row_to_protobuf(row, result, ['f3', 'f4', 'segment_id', 'event_subgroup_id', 'finish_time_str', 'f14', 'f17', 'f18'])
|
||||
|
||||
return results.SerializeToString(), 200
|
||||
|
||||
|
||||
@app.route('/relay/segment-results', methods=['GET'])
|
||||
def relay_segment_results():
|
||||
return handle_segment_results(request)
|
||||
|
||||
|
||||
@app.route('/api/segment-results', methods=['GET', 'POST'])
|
||||
@jwt_to_session_cookie
|
||||
@login_required
|
||||
def api_segment_results():
|
||||
#Checks that online player has values for ghosts and player_id
|
||||
player_id = current_user.player_id
|
||||
if request.method == 'POST' and player_id != current_user.player_id:
|
||||
return '', 401
|
||||
return handle_segment_results(request)
|
||||
|
||||
|
||||
@app.route('/live-segment-results-service/leaders', methods=['GET'])
|
||||
def live_segment_results_service_leaders():
|
||||
results = segment_result_pb2.SegmentResults()
|
||||
|
||||
Reference in New Issue
Block a user