Syncing latest changes between public/private.

This commit is contained in:
Eric Froemling 2020-03-12 19:07:24 -07:00
parent fedc897a38
commit 3c70c68257
5 changed files with 86 additions and 75 deletions

View File

@ -1,54 +1,54 @@
### 1.5.0 (20001) ### 1.5.0 (20001)
- Ported the entire scripting layer from Python 2 to to Python 3 (currently 3.5 to 3.7 depending on platform). There's some significant changes going from python 2 to 3 (new print statement, string behavior, etc), but these are well documented online, so please read up as needed. This should provide us some nice benefits and future-proofs everything. (my janky 2.7 custom Python builds were getting a little long in the tooth). - Ported the entire scripting layer from Python 2 to to Python 3 (currently at 3.7, and I intend to keep this updated to the latest widely-available release). There's some significant changes going from python 2 to 3 (new print statement, string behavior, etc), but these are well documented online, so please read up as needed. This should provide us some nice benefits and future-proofs everything. (my janky 2.7 custom Python builds were getting a little long in the tooth).
- Refactored all script code to be PEP8 compliant (python coding standards). Basically, this means that stuff that was camel-case (fooBar) is now a single word or underscores (foobar / foo_bar). There are a few minor exceptions such as existing resource and media filenames, but in general old code can be ported by taking a pass through and killing the camel-case. I know this is a bit of a pain in the ass, but it'll let us use things like pylint and just be more consistent with the rest of the Python world. - Refactored all script code to be PEP8 compliant (Python coding standards). Basically, this means that stuff that was camel-case (fooBar) is now a single word or underscores (foobar / foo_bar). There are a few minor exceptions such as existing resource and media filenames, but in general old code can be ported by taking a pass through and killing the camel-case. I know this is a bit of a pain in the ass, but it'll let us use things like Pylint and just be more consistent with the rest of the Python world.
- On a related note, I'm now using 'yapf' to keep my Python code formatted nicely (using pep8 style); I'd recommend checking it out if you're doing a lot of scripting as its a great time-saver. - On a related note, I'm now using 'yapf' to keep my Python code formatted nicely (using pep8 style); I'd recommend checking it out if you're doing a lot of scripting as its a great time-saver.
- On another related note, I'm trying to confirm to Google's recommendations for Python code (search 'Google Python Style Guide'). There are some good bits of wisdom in there so I recommend at least skimming through it. - On another related note, I'm trying to confirm to Google's recommendations for Python code (search 'Google Python Style Guide'). There are some good bits of wisdom in there so I recommend at least skimming through it.
- And as one last related note, I'm now running Pylint on all my own Python code. Highly recommended if you are doing serious scripting, as it can make Python almost feel as type-safe as C++. - And as one last related note, I'm now running Pylint on all my own Python code. Highly recommended if you are doing serious scripting, as it can make Python almost feel as type-safe as C++.
- The minimum required android version is now 5.0 (a requirement of the Python 3 builds I'm using) - The minimum required android version will now be 5.0 (a requirement of the Python 3 builds I'm using)
- Minimum required macOS version is now 10.13 (for similar reasons) - Minimum required macOS version is now 10.13 (for similar reasons)
- 'bsInternal' module is now '_bs' (better lines up with standard Python practices) - 'bsInternal' module is now '_ba' (better lines up with standard Python practices)
- bs.writeConfig() and bs.applySettings() are no more. There is now bs.app.config which is basically a fancy dict class with some methods added such as commit() and apply() - bs.writeConfig() and bs.applySettings() are no more. There is now ba.app.config which is basically a fancy dict class with some methods added such as commit() and apply()
- bs.getEnvironment() is no more; the values there are now available through bs.app (see notes down further) - bs.getEnvironment() is no more; the values there are now available through ba.app (see notes down further)
- fixed the mac build so command line input works again when launched from a terminal - Fixed the mac build so command line input works again when launched from a terminal
- renamed 'exceptionOnNone' arg to 'doraise' in various calls - Renamed 'exceptionOnNone' arg to 'doraise' in various calls.
- bs.emitBGDynamics() is now bs.emitfx() - bs.emitBGDynamics() is now ba.emitfx()
- bs.shakeCamera() is now bs.camerashake() - bs.shakeCamera() is now ba.camerashake()
- various other minor name changes (bs.getUIBounds() -> bs.app.uibounds, etc). I'm keeping old and new Python API docs around for now so you can compare as needed. - Various other minor name changes (bs.getUIBounds() -> ba.app.uibounds, etc). I'm keeping old and new Python API docs around for now so you can compare as needed.
- renamed bot classes based on their actions instead of their appearances (ie: PirateBot -> ExplodeyBot) - Renamed bot classes based on their actions instead of their appearances (ie: PirateBot -> ExplodeyBot)
- bs.getSharedObject() is now bs.stdobj() - bs.getSharedObject() is now ba.stdobj()
- removed bs.uni(), bs.utf8(), bs.uni_to_ints(), and bs.uni_from_ints() which are no longer needed due to Python 3's better string handling - Removed bs.uni(), bs.utf8(), bs.uni_to_ints(), and bs.uni_from_ints() which are no longer needed due to Python 3's better string handling.
- removed bs.SecureInt since it didn't do much to slow down hackers and hurts code readability - Removed bs.SecureInt since it didn't do much to slow down hackers and hurts code readability.
- renamed 'finalize' to 'expire' for actors and activities. 'Finalize' sounds too much like a destructor, which is not really what that is. - Renamed 'finalize' to 'expire' for actors and activities. 'Finalize' sounds too much like a destructor, which is not really what that is.
- bs.getMapsSupportingPlayType() is now simply bs.getmaps() (I might want to add more filter options to it besides just play-type) - bs.getMapsSupportingPlayType() is now simply ba.getmaps(). I might want to add more filter options to it besides just play-type, hence the rename.
- changed the concept of 'game', 'net', and 'real' times to 'sim', 'base', and 'real'. See time function docs for specifics. Also cleared up a few ambiguities over what can be used where. - Changed the concept of 'game', 'net', and 'real' times to 'sim', 'base', and 'real'. See time function docs for specifics. Also cleared up a few ambiguities over what can be used where.
- I'm converting all scripting functions to operate on floating-point seconds by default instead of integer milliseconds. This will let us support more accurate simulations later and is just cleaner in my opinion. To keep existing calls working you should be able to add timeformat='ms' and you'll get the old behavior (or multiply your time values by 0.001). Specific notes listed below. - I'm converting all scripting functions to operate on floating-point seconds by default instead of integer milliseconds. This will let us support more accurate simulations later and is just cleaner I feel. To keep existing calls working you should be able to add timeformat='ms' and you'll get the old behavior (or multiply your time values by 0.001). Specific notes listed below.
- bs.Timer now takes its 'time' arg as seconds instead of milliseconds. To port old calls, add: timeformat='ms' to each call (or multiply your input by 0.001) - ba.Timer now takes its 'time' arg as seconds instead of milliseconds. To port old calls, add: timeformat='ms' to each call (or multiply your input by 0.001)
- bs.animate() now takes times in seconds and its 'driver' arg is now 'timetype' for consistency with other time functions. To port existing code you can pass timeformat='ms' to keep the old milliseconds based behavior - ba.animate() now takes times in seconds and its 'driver' arg is now 'timetype' for consistency with other time functions. To port existing code you can pass timeformat='ms' to keep the old milliseconds based behavior.
- ditto for bs.animate_array() - ditto for ba.animate_array()
- bs.Activity.end() now takes seconds instead of milliseconds as its delay arg - ba.Activity.end() now takes seconds instead of milliseconds as its delay arg.
- TNTSpawner now also takes seconds instead of milliseconds for respawn_time - TNTSpawner now also takes seconds instead of milliseconds for respawn_time.
- there is a new bs.timer() function which is used for all one-off timer creation. It has the same args as the bs.Timer() class constructor. - There is a new ba.timer() function which is used for all one-off timer creation. It has the same args as the ba.Timer() class constructor.
- bs.gameTimer() is no more. Pass timeformat='ms' to bs.timer() if you need to recreate its behavior. - bs.gameTimer() is no more. Pass timeformat='ms' to ba.timer() if you need to recreate its behavior.
- bs.netTimer() is no more. Pass timetype='base' and timeformat='ms' to bs.timer() if you need to recreate its behavior. - bs.netTimer() is no more. Pass timetype='base' and timeformat='ms' to ba.timer() if you need to recreate its behavior.
- bs.realTimer() is no more. Pass timetype='real' and timeformat='ms' to bs.timer() if you need to recreate its behavior. - bs.realTimer() is no more. Pass timetype='real' and timeformat='ms' to ba.timer() if you need to recreate its behavior.
- there is a new bs.time() function for getting time values; it has consistent args with the new bs.timer() and bs.Timer() calls - There is a new ba.time() function for getting time values; it has consistent args with the new ba.timer() and ba.Timer() calls.
- bs.getGameTime() is no more. Pass timeformat='ms' to bs.time() if you need to recreate its behavior - bs.getGameTime() is no more. Pass timeformat='ms' to ba.time() if you need to recreate its behavior.
- bs.getNetTime() is no more. Pass timetype='base' and timeformat='ms' to bs.time() if you need to recreate its behavior - bs.getNetTime() is no more. Pass timetype='base' and timeformat='ms' to ba.time() if you need to recreate its behavior.
- bs.getRealTime() is no more. Pass timetype='real' and timeformat='ms' to bs.time() if you need to recreate its behavior - bs.getRealTime() is no more. Pass timetype='real' and timeformat='ms' to ba.time() if you need to recreate its behavior.
- bs.getTimeString() is now just bs.timestring(), and accepts seconds by default (pass timeformat='ms' to keep old calls working) - bs.getTimeString() is now just ba.timestring(), and accepts seconds by default (pass timeformat='ms' to keep old calls working).
- bs.callInGameThread() has been replaced by an optional 'from_other_thread' arg for bs.pushcall() - bs.callInGameThread() has been replaced by an optional 'from_other_thread' arg for ba.pushcall()
- there is now a special bs.UNHANDLED value that handlemessage() calls should return any time they don't handle a passed message. This will allow fallback message types and other nice things in the future. - There is now a special ba.UNHANDLED value that handlemessage() calls should return any time they don't handle a passed message. This will allow fallback message types and other nice things in the future.
- wired the boolean operator up to bs.Actor's exists() method, so now a simple "if mynode" will do the right thing for both Actors and None values instead of having to explicitly check for both - Wired the boolean operator up to ba.Actor's exists() method, so now a simple "if mynode" will do the right thing for both Actors and None values instead of having to explicitly check for both.
- ditto for bs.Node; you can now just do 'if mynode' which will do the right thing for both a dead Node or None - Ditto for ba.Node; you can now just do 'if mynode' which will do the right thing for both a dead Node or None.
- ditto for bs.InputDevice, bs.Widget, bs.Player - Ditto for ba.InputDevice, ba.Widget, ba.Player
- added a bs.App class accessible via bs.app; will be migrating global app values there instead of littering python modules with globals. The only remaining module globals should be all-caps public 'constants' - Added a bs.App class accessible via ba.app; will be migrating global app values there instead of littering python modules with globals. The only remaining module globals should be all-caps public 'constants'
- 'Internal' methods and classes living in _bs and elsewhere no longer start with underscores. They are now simply marked with '(internal)' in their docstrings. 'Internal' bits are likely to have janky interfaces and can change without warning, so be wary of using them. If you find yourself depending on some internal thing often, please let me know and I can try to clean it up and make it 'public'. - 'Internal' methods and classes living in _ba and elsewhere no longer start with underscores. They are now simply marked with '(internal)' in their docstrings. 'Internal' bits are likely to have janky interfaces and can change without warning, so be wary of using them. If you find yourself depending on some internal thing often, please let me know and I can try to clean it up and make it 'public'.
- bs.getLanguage() is no more; that value is now accessible via bs.appstate().language - bs.getLanguage() is no more; that value is now accessible via ba.app.language
- bs.Actor now accepts an optional 'node' arg which it will store as self.node if passed. Its default DieMessage() and exists() handlers will use self.node if it exists. This removes the need for a separate NodeActor() for simple cases. - bs.Actor now accepts an optional 'node' arg which it will store as self.node if passed. Its default DieMessage() and exists() handlers will use self.node if it exists. This removes the need for a separate NodeActor() for simple cases.
- bs.NodeActor is no more (it can simply be replaced with bs.Actor()) - bs.NodeActor is no more (it can simply be replaced with ba.Actor())
- bs.playMusic() is now bs.setmusic() which better fits its functionality (it sometimes just continues playing or stops playing) - bs.playMusic() is now ba.setmusic() which better fits its functionality (it sometimes just continues playing or stops playing).
- the bs.Vector class is no more; in its place is a shiny new bs.Vec3 which is implemented internally in C++ so its nice and speedy. Will probably update certain things like vector node attrs to support this class in the future since it makes vector math nice and convenient. - The bs.Vector class is no more; in its place is a shiny new ba.Vec3 which is implemented internally in C++ so its nice and speedy. Will probably update certain things like vector node attrs to support this class in the future since it makes vector math nice and convenient.
- ok you get the point.. - Ok you get the point..
### 1.4.150 (14369) ### 1.4.150 (14369)
- Telnet port can now be specified in the config - Telnet port can now be specified in the config

View File

@ -347,10 +347,10 @@ test: prereqs
# Run tests with any caching disabled. # Run tests with any caching disabled.
test-full: test test-full: test
# Some individual tests for iterating. # Iterating on individual tests with extra debug output enabled.
test-assetmanager: test-assetmanager:
@tools/snippets pytest \ @tools/snippets pytest -o log_cli=true -o log_cli_level=debug -s -v \
-s -v tests/test_ba/test_assetmanager.py::test_assetmanager tests/test_ba/test_assetmanager.py::test_assetmanager
# Tell make which of these targets don't represent files. # Tell make which of these targets don't represent files.
.PHONY: test test-full .PHONY: test test-full

View File

@ -30,7 +30,6 @@ import weakref
import time import time
import os import os
import sys import sys
# import atexit
from efro import entity from efro import entity
@ -58,22 +57,16 @@ class AssetManager:
print('AssetManager()') print('AssetManager()')
assert isinstance(rootdir, Path) assert isinstance(rootdir, Path)
self._rootdir = rootdir self._rootdir = rootdir
self._shutting_down = False self._started = False
if not self._rootdir.is_dir(): if not self._rootdir.is_dir():
raise RuntimeError(f'Provided rootdir does not exist: "{rootdir}"') raise RuntimeError(f'Provided rootdir does not exist: "{rootdir}"')
self.load_state() self.load_state()
# atexit.register(self._at_exit)
def __del__(self) -> None: def __del__(self) -> None:
self._shutting_down = True
self.update()
print('~AssetManager()') print('~AssetManager()')
if self._started:
# @staticmethod logging.warning('AssetManager dying in a started state.')
# def _at_exit() -> None:
# print('HELLO FROM SHUTDOWN')
def launch_gather( def launch_gather(
self, self,
@ -89,9 +82,24 @@ class AssetManager:
def update(self) -> None: def update(self) -> None:
"""Can be called periodically to perform upkeep.""" """Can be called periodically to perform upkeep."""
# Currently we always write state when shutting down. def start(self) -> None:
if self._shutting_down: """Tell the manager to start working.
self.save_state()
This will initiate network activity and other processing.
"""
if self._started:
logging.warning('AssetManager.start() called on running manager.')
self._started = True
def stop(self) -> None:
"""Tell the manager to stop working.
All network activity should be ceased before this function returns.
"""
if not self._started:
logging.warning('AssetManager.stop() called on stopped manager.')
self._started = False
self.save_state()
@property @property
def rootdir(self) -> Path: def rootdir(self) -> Path:
@ -118,6 +126,7 @@ class AssetManager:
def save_state(self) -> None: def save_state(self) -> None:
"""Save state to disk (if possible).""" """Save state to disk (if possible)."""
print('AMAN SAVING STATE') print('AMAN SAVING STATE')
try: try:
with open(self.state_path, 'w') as outfile: with open(self.state_path, 'w') as outfile:
@ -133,7 +142,9 @@ class AssetGather:
self._manager = weakref.ref(manager) self._manager = weakref.ref(manager)
self._valid = True self._valid = True
print('AssetGather()') print('AssetGather()')
fetch_url("http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz", # url = 'https://files.ballistica.net/bombsquad/promo/BSGamePlay.mov'
url = 'http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz'
fetch_url(url,
filename=Path(manager.rootdir, 'testdl'), filename=Path(manager.rootdir, 'testdl'),
asset_gather=self) asset_gather=self)
print('fetch success') print('fetch success')
@ -151,9 +162,7 @@ class AssetGather:
def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None: def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
"""Fetch a given url to a given filename for a given AssetGather. """Fetch a given url to a given filename."""
This """
import socket import socket
@ -176,13 +185,12 @@ def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
# should ensure a minimum rate for the loop and this will affect # should ensure a minimum rate for the loop and this will affect
# the maximum. Perhaps we should aim for a few cycles per second on # the maximum. Perhaps we should aim for a few cycles per second on
# an average connection?.. # an average connection?..
block_sz = 1024 * 100 * 2 block_sz = 1024 * 1024
time_outs = 0 time_outs = 0
while True: while True:
try: try:
data = ureq.read(block_sz) data = ureq.read(block_sz)
except socket.timeout: except socket.timeout:
# File has not had activity in max seconds. # File has not had activity in max seconds.
if time_outs > 3: if time_outs > 3:
print("\n\n\nsorry -- try back later") print("\n\n\nsorry -- try back later")

View File

@ -1,6 +1,6 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<!--DOCSHASH=5b1863340d0e423b597b188ee7c7ac23--> <!--DOCSHASH=0ccb27f0a14c6ad6f07acd13b8bd2fbb-->
<h4><em>last updated on 2020-03-11 for Ballistica version 1.5.0 build 20001</em></h4> <h4><em>last updated on 2020-03-12 for Ballistica version 1.5.0 build 20001</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>

View File

@ -41,16 +41,19 @@ def test_assetmanager() -> None:
"""Testing.""" """Testing."""
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
man = AssetManager(rootdir=Path(tmpdir)) manager = AssetManager(rootdir=Path(tmpdir))
wref = weakref.ref(man) wref = weakref.ref(manager)
manager.start()
gather = man.launch_gather(packages=['a@2'], gather = manager.launch_gather(packages=['a@2'],
flavor=AssetPackageFlavor.DESKTOP, flavor=AssetPackageFlavor.DESKTOP,
account_token='dummytoken') account_token='dummytoken')
wref2 = weakref.ref(gather) wref2 = weakref.ref(gather)
manager.stop()
# Make sure nothing is keeping itself alive # Make sure nothing is keeping itself alive
del man del manager
del gather del gather
assert wref() is None assert wref() is None
assert wref2() is None assert wref2() is None