-
Notifications
You must be signed in to change notification settings - Fork 7
Modmenu pagination #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Modmenu pagination #123
Changes from all commits
5f79ae5
639dbdc
2c8e315
9e87fdd
1cc566a
bfd9f1f
7246c68
dd96c57
f0f534f
02b1fd1
40c2126
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| from cStringIO import StringIO | ||
| import zipfile | ||
| from collections import namedtuple | ||
| import threading | ||
|
|
||
| import renpy | ||
| from renpy.audio.music import stop as _stop_music | ||
|
|
@@ -21,6 +22,7 @@ | |
| if workshop_enabled: | ||
| from steam_workshop.steam_config import has_valid_signature | ||
| import steam_workshop.steamhandler as steamhandler | ||
| import steamhandler_extensions | ||
|
|
||
|
|
||
| BRANCHES_API = "https://api.github.com/repos/AWSW-Modding/AWSW-Modtools/branches" | ||
|
|
@@ -98,21 +100,128 @@ def github_downloadable_mods(): | |
| return sorted(data, key=lambda mod: mod[1].lower()) | ||
|
|
||
|
|
||
| @cache | ||
| def steam_downloadable_mods(): | ||
| # A different format, | ||
| # (id, mod_name, author, desc, image_url) | ||
| mods = [] | ||
| for mod in sorted(steamhandler.get_instance().GetAllItems(), key=lambda mod: mod[1]): | ||
| file_id = mod[0] | ||
| create_time, modify_time, signature = mod[5:8] | ||
| is_valid, verified = has_valid_signature(file_id, create_time, modify_time, signature) | ||
| if is_valid: | ||
| mods.append(list(mod[:5])) | ||
| mods[-1][3] += "\n\nVerified by {}".format(verified.username.replace("<postmaster@example.com>", "")) | ||
|
|
||
| class SteamModlist: | ||
| """Manages the steam modlist, as is gotten by the steam_downloadable_mods method. | ||
| It supports loading the modlist in a separate thread via the load method, | ||
| And caching such results. | ||
| This is needed as loading the modlist takes quite a while, | ||
| And is an operation we would much rather do at startup, without delaying anything else. | ||
| Any exceptions raised in the loading process will be available through the get_exception() method. | ||
| Once the load finishes, Only one of the get() and get_exception() methods will return a value, while the other will return None. | ||
| If the load is successful, then get() will return a value. if the load raised an exception, then get_exception() will return a value. | ||
| """ | ||
|
|
||
| def __init__(self): | ||
| self._loading_thread = None | ||
| self._loading_thread_lock = threading.Lock() | ||
| self._loaded_data = None | ||
| self._exception = None | ||
| self._is_done = threading.Event() | ||
| return | ||
|
|
||
| def _loading_function(self): | ||
| """Loads and verifies the steam modlist data. | ||
| It must take no arguments, and return a single value: the loaded data. | ||
| It may raise an exception, in which case it'll be stored in self._exception. | ||
| """ | ||
|
|
||
| # A different format, | ||
| # (id, mod_name, author, desc, image_url) | ||
|
|
||
| # This uses GetAllItems(), Which is affected by the QueryApi crash. | ||
| # therefore, steamhandler_extensions are preferred | ||
| mods = [] | ||
| for mod in sorted(steamhandler_extensions.get_instance().GetAllItems(), key=lambda mod: mod[1]): | ||
| if mod[1] == "Modtools": | ||
| continue # The modtools themselves need not be here (as they're already present and can't be removed using themselves), nor should the signing system complain about them... | ||
| file_id = mod[0] | ||
| create_time, modify_time, signature = mod[5:8] | ||
| is_valid, verified = has_valid_signature(file_id, create_time, modify_time, signature) | ||
| if is_valid: | ||
| mods.append(list(mod[:5])) | ||
| mods[-1][3] += "\n\nVerified by {}".format(verified.username.replace("<postmaster@example.com>", "")) | ||
| else: | ||
| print "NOT VALID SIG", mod[1] # Note: printing only the mod name, instead of the whole thing SIGNIFICANTLY speeds up this call | ||
| return mods | ||
|
|
||
| def _load_and_set(self): | ||
| """Calls the _loading_function and sets the internal values based on it's results.""" | ||
| try: | ||
| mods = self._loading_function() | ||
| self._loaded_data = mods | ||
| print "Finished steam modlist load without errors" | ||
| except Exception as e: | ||
| print "Finished steam modlist load with errors" | ||
| self._exception = e | ||
| self._exception.traceback = sys.exc_info()[2] | ||
|
|
||
| self._is_done.set() | ||
| print "Done loading steam modlist" | ||
| return | ||
|
|
||
| def load(self): | ||
| """Starts loading the steam modlist data if it is not already being loaded. | ||
| This method starts a thread which loads the modlist data. | ||
| It guarantees that for any number of repeated calls to it from any number of threads, only one loading thread will be started. | ||
| Once the data is loaded, it is available through the get method. | ||
| """ | ||
| with self._loading_thread_lock: | ||
| if self._loading_thread is None: | ||
| print "Steam modlist thread not present, Starting..." | ||
| self._loading_thread = threading.Thread(target=self._load_and_set, name=u"Thread-load-{}".format(self._load_and_set.__name__)) | ||
| self._loading_thread.start() | ||
| else: | ||
| print "Steam modlist thread already present" | ||
| return self._loading_thread | ||
|
|
||
| def get(self): | ||
| """Get the steam modlist data. | ||
| If the data has already loaded, this method returns with it immediately, | ||
| Otherwise, load() is called, and this method blocks until the completion of the data loading thread. | ||
| """ | ||
| if self._is_done.is_set(): | ||
| # Note: while the value of is_set can change between checking it here and referring to _loaded_data, | ||
| # It can only change from False to True. | ||
| # In that case the load() method has been called before and is currently finishing, | ||
| # And it'll be called again here, ignored, and _is_done will be waited upon, which will finish only once _loaded_data is available. | ||
|
|
||
| print "Steam modlist data already available" | ||
| else: | ||
| print "Steam modlist data not available, calling load" | ||
| self.load() | ||
| self._is_done.wait() | ||
| print "Loading done, fetching data" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem to handle exceptions?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is designed such that any exception raised in loading (which can only come from self._loading_function) is captured by self._load_and_set, and made available through self.get_exception. as the loading is done away from the main thread, it shouldn't raise exceptions in the main thread, and it's not able to report them by itself. this way, the error information is present, and the responsibility for handling them is on the caller. |
||
| return self._loaded_data | ||
|
|
||
| def get_exception(self): | ||
| """Get the raised exception, If the load raised an exception. | ||
| If the data has already loaded, this method returns with it immediately, | ||
| Otherwise, load() is called, and this method blocks until the completion of the data loading thread. | ||
| """ | ||
| if self._is_done.is_set(): | ||
| # Identical logic to self.get() | ||
|
|
||
| print "Steam modlist exception already available" | ||
| else: | ||
| print "NOT VALID SIG", mod | ||
| return mods | ||
| print "Steam modlist data not available, calling load" | ||
| self.load() | ||
| self._is_done.wait() | ||
| print "Loading done, fetching exception" | ||
| return self._exception | ||
|
|
||
| def is_done(self): | ||
| return self._is_done.is_set() | ||
|
|
||
| def wait(self, timeout=None): | ||
| return self._is_done.wait(timeout) | ||
|
|
||
|
|
||
| steam_mod_list = SteamModlist() | ||
|
|
||
|
|
||
| def steam_downloadable_mods(): | ||
| return steam_mod_list.get() | ||
|
|
||
|
|
||
| def download_github_mod(download_link, name, show_download=True, reload_script=True): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this by mod id instead?