Skip to content

Commit 9e03693

Browse files
committed
Added automatic version update notes
Added a new listener (VersionUpdate) that gets called whenever a new Source.Python version is available Added functions to check the latest Source.Python version
1 parent f45b095 commit 9e03693

5 files changed

Lines changed: 123 additions & 1 deletion

File tree

addons/source-python/packages/source-python/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
===============================================================================
66
Source Python
7-
Copyright (C) 2012 Source Python Development Team. All rights reserved.
7+
Copyright (C) 2012-2015 Source Python Development Team. All rights reserved.
88
===============================================================================
99
1010
This program is free software; you can redistribute it and/or modify it

addons/source-python/packages/source-python/core/settings.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,23 @@ def _check_base_settings(self):
121121
self['BASE_SETTINGS'].comments['language'] = _core_strings[
122122
'language'].get_string(self._language).splitlines()
123123

124+
def _check_version_settings(self):
125+
"""Add version settings if they are missing."""
126+
if 'VERSION_SETTINGS' not in self:
127+
self['VERSION_SETTINGS'] = {}
128+
129+
if 'check_for_update' not in self['VERSION_SETTINGS']:
130+
self['VERSION_SETTINGS']['check_for_update'] = '1'
131+
132+
if 'notify_on_update' not in self['VERSION_SETTINGS']:
133+
self['VERSION_SETTINGS']['notify_on_update'] = '1'
134+
135+
self['VERSION_SETTINGS'].comments['check_for_update'] = _core_strings[
136+
'check_for_update'].get_string(self._language).splitlines()
137+
138+
self['VERSION_SETTINGS'].comments['notify_on_update'] = _core_strings[
139+
'notify_on_update'].get_string(self._language).splitlines()
140+
124141
def _check_auth_settings(self):
125142
"""Add auth settings if they are missing."""
126143
# Are there any auth settings in the file?

addons/source-python/packages/source-python/core/version.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# =============================================================================
66
# >> IMPORTS
77
# =============================================================================
8+
# Python Imports
9+
from urllib.request import urlopen
810
# Source.Python Imports
911
# Cvars
1012
from cvars.public import PublicConVar
@@ -14,6 +16,9 @@
1416
# >> ALL DECLARATION
1517
# =============================================================================
1618
__all__ = ('VERSION',
19+
'get_last_successful_build_number',
20+
'is_newer_version_available',
21+
'is_unversioned',
1722
)
1823

1924

@@ -22,9 +27,38 @@
2227
# =============================================================================
2328
VERSION = 'unversioned'
2429

30+
# TODO: Update this url when the new subdomain has been created
31+
LAST_SUCCESSFUL_BUILD_NUMBER_URL = ('http://build.affecta.net/job/Source.Pyth'
32+
'on/api/xml?xpath=/freeStyleProject/lastSuccessfulBuild/number')
33+
2534

2635
# =============================================================================
2736
# >> GLOBAL VARIABLES
2837
# =============================================================================
2938
_sp_version = PublicConVar(
3039
'sp_version', VERSION, description='Source.Python version')
40+
41+
42+
# =============================================================================
43+
# >> FUNCTIONS
44+
# =============================================================================
45+
def get_last_successful_build_number(timeout=3):
46+
"""Return the latest successful build number as an integer."""
47+
with urlopen(LAST_SUCCESSFUL_BUILD_NUMBER_URL, timeout=timeout) as url:
48+
# Remove the <number></number> tags, so we just have the build number
49+
return int(url.read()[8:-9])
50+
51+
def is_unversioned():
52+
"""Return True if the current version is set to 'unversioned'."""
53+
return VERSION == 'unversioned'
54+
55+
def is_newer_version_available():
56+
"""Return a tuple that contains a boolean that indicates if a new
57+
Source.Python version is available and an integer that defines the latest
58+
successful build number.
59+
60+
If the current version is set to 'unversioned', it will return True for
61+
the first value.
62+
"""
63+
build_number = get_last_successful_build_number()
64+
return (is_unversioned() or VERSION < build_number, build_number)

addons/source-python/packages/source-python/listeners/__init__.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22

33
"""Provides listener based functionality."""
44

5+
# TODO: Fix name conflict with _ListenerManager
6+
57
# =============================================================================
68
# >> IMPORTS
79
# =============================================================================
10+
# Python Imports
11+
import socket
12+
from urllib.error import URLError
813
# Source.Python Imports
14+
# Cvars
15+
from cvars import ConVar
916
# Core
1017
from core import AutoUnload
18+
from core.settings import _core_settings
19+
from core.version import is_newer_version_available
20+
from core.version import is_unversioned
1121
# Loggers
1222
from loggers import _sp_logger
1323

@@ -17,6 +27,7 @@
1727
# =============================================================================
1828
# Source.Python Imports
1929
# Listeners
30+
from _listeners import _ListenerManager
2031
from _listeners import client_active_listener_manager
2132
from _listeners import client_connect_listener_manager
2233
from _listeners import client_disconnect_listener_manager
@@ -67,6 +78,7 @@
6778
'OnQueryCvarValueFinished',
6879
'ServerActivate',
6980
'Tick',
81+
'VersionUpdate',
7082
'client_active_listener_manager',
7183
'client_connect_listener_manager',
7284
'client_disconnect_listener_manager',
@@ -89,6 +101,7 @@
89101
'on_query_cvar_value_finished_listener_manager',
90102
'server_activate_listener_manager',
91103
'tick_listener_manager',
104+
'version_update_listener_manager',
92105
)
93106

94107

@@ -97,6 +110,18 @@
97110
# =============================================================================
98111
# Get the sp.listeners logger
99112
listeners_logger = _sp_logger.listeners
113+
version_update_listener_manager = _ListenerManager()
114+
115+
_check_for_update = ConVar('sp_check_for_update',
116+
_core_settings['VERSION_SETTINGS']['check_for_update'],
117+
description='Enable/disable checking for version updates.',
118+
min_value=0, max_value=1)
119+
120+
_notify_on_update = ConVar('sp_notify_on_update',
121+
_core_settings['VERSION_SETTINGS']['notify_on_update'],
122+
description=('Log a warning when a Source.Python update is available. Req'
123+
'uires sp_check_for_update to be set to 1.'),
124+
min_value=0, max_value=1)
100125

101126

102127
# =============================================================================
@@ -143,6 +168,11 @@ def name(self):
143168
"""Return the class name of the instance."""
144169
return self.__class__.__name__
145170

171+
@property
172+
def manager(self):
173+
"""Return a _ListenerManager object."""
174+
raise NotImplementedError('Must be implemented by a subclass.')
175+
146176
def _unload_instance(self):
147177
"""Unregister the listener."""
148178
# Log the unregistering
@@ -306,3 +336,36 @@ class Tick(_ListenerManager):
306336
"""Register/unregister a Tick listener."""
307337

308338
manager = tick_listener_manager
339+
340+
341+
class VersionUpdate(_ListenerManager):
342+
343+
"""Register/unregister a version update listener."""
344+
345+
manager = version_update_listener_manager
346+
347+
348+
# =============================================================================
349+
# >> CALLBACKS
350+
# =============================================================================
351+
@LevelInit
352+
def _on_level_init(map_name):
353+
"""Called when a new map gets initialized."""
354+
if not _check_for_update.get_int():
355+
return
356+
357+
try:
358+
update_available, version = is_newer_version_available()
359+
except (URLError, socket.timeout):
360+
return
361+
362+
if not update_available:
363+
return
364+
365+
if _notify_on_update.get_int():
366+
# TODO: Add translations
367+
listeners_logger.log_warning(
368+
'A new Source.Python version is available!')
369+
370+
version_update_listener_manager.notify(
371+
update_available, version, is_unversioned())

resource/source-python/translations/_core/core_settings_strings.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,11 @@ de = 'Bestimme die Befehle, die als Clientbefehle verwendet werden sollen.'
193193
fr = 'Définit les commandes clientes à utiliser.'
194194
nl = "Stel de commando namen in te gebruiken als client chat commando's."
195195
ru = 'Установите названия команд, которые будут использоваться как команды клиента.'
196+
197+
[check_for_update]
198+
en = 'Enable/disable checking for Source.Python version updates.'
199+
de = 'Aktiviere/deaktiviere die Suche nach neuen Source.Python Versionen.'
200+
201+
[notify_on_update]
202+
en = 'Log a warning when a Source.Python update is available. Requires check_for_update to be set to 1.'
203+
de = 'Logge eine Warnung, wenn eine neue Source.Python Version verfügbar ist. check_for_update muss dafür auf 1 gesetzt sein.'

0 commit comments

Comments
 (0)