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)
- 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).
- 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.
- 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.
- 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.
- 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)
- 'bsInternal' module is now '_bs' (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.getEnvironment() is no more; the values there are now available through bs.app (see notes down further)
- fixed the mac build so command line input works again when launched from a terminal
- renamed 'exceptionOnNone' arg to 'doraise' in various calls
- bs.emitBGDynamics() is now bs.emitfx()
- bs.shakeCamera() is now bs.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.
- renamed bot classes based on their actions instead of their appearances (ie: PirateBot -> ExplodeyBot)
- bs.getSharedObject() is now bs.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.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.
- bs.getMapsSupportingPlayType() is now simply bs.getmaps() (I might want to add more filter options to it besides just play-type)
- 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.
- 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)
- 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
- ditto for bs.animate_array()
- bs.Activity.end() now takes seconds instead of milliseconds as its delay arg
- 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.
- bs.gameTimer() is no more. Pass timeformat='ms' to bs.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.realTimer() is no more. Pass timetype='real' and timeformat='ms' to bs.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
- bs.getGameTime() is no more. Pass timeformat='ms' to bs.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.getRealTime() is no more. Pass timetype='real' and timeformat='ms' to bs.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.callInGameThread() has been replaced by an optional 'from_other_thread' arg for bs.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.
- 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
- 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 bs.InputDevice, bs.Widget, bs.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'
- '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'.
- bs.getLanguage() is no more; that value is now accessible via bs.appstate().language
- 'bsInternal' module is now '_ba' (better lines up with standard Python practices)
- 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 ba.app (see notes down further)
- Fixed the mac build so command line input works again when launched from a terminal
- Renamed 'exceptionOnNone' arg to 'doraise' in various calls.
- bs.emitBGDynamics() is now ba.emitfx()
- bs.shakeCamera() is now ba.camerashake()
- 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)
- 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.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.
- 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.
- 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.
- 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)
- 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 ba.animate_array()
- ba.Activity.end() now takes seconds instead of milliseconds as its delay arg.
- TNTSpawner now also takes seconds instead of milliseconds for respawn_time.
- 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 ba.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 ba.timer() if you need to recreate its behavior.
- 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 ba.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 ba.time() if you need to recreate its behavior.
- 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 ba.pushcall()
- 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 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 ba.Node; you can now just do 'if mynode' which will do the right thing for both a dead Node or None.
- Ditto for ba.InputDevice, ba.Widget, ba.Player
- 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 _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 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.NodeActor is no more (it can simply be replaced with bs.Actor())
- bs.playMusic() is now bs.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.
- ok you get the point..
- bs.NodeActor is no more (it can simply be replaced with ba.Actor())
- 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 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..
### 1.4.150 (14369)
- Telnet port can now be specified in the config

View File

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

View File

@ -30,7 +30,6 @@ import weakref
import time
import os
import sys
# import atexit
from efro import entity
@ -58,22 +57,16 @@ class AssetManager:
print('AssetManager()')
assert isinstance(rootdir, Path)
self._rootdir = rootdir
self._shutting_down = False
self._started = False
if not self._rootdir.is_dir():
raise RuntimeError(f'Provided rootdir does not exist: "{rootdir}"')
self.load_state()
# atexit.register(self._at_exit)
def __del__(self) -> None:
self._shutting_down = True
self.update()
print('~AssetManager()')
# @staticmethod
# def _at_exit() -> None:
# print('HELLO FROM SHUTDOWN')
if self._started:
logging.warning('AssetManager dying in a started state.')
def launch_gather(
self,
@ -89,9 +82,24 @@ class AssetManager:
def update(self) -> None:
"""Can be called periodically to perform upkeep."""
# Currently we always write state when shutting down.
if self._shutting_down:
self.save_state()
def start(self) -> None:
"""Tell the manager to start working.
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
def rootdir(self) -> Path:
@ -118,6 +126,7 @@ class AssetManager:
def save_state(self) -> None:
"""Save state to disk (if possible)."""
print('AMAN SAVING STATE')
try:
with open(self.state_path, 'w') as outfile:
@ -133,7 +142,9 @@ class AssetGather:
self._manager = weakref.ref(manager)
self._valid = True
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'),
asset_gather=self)
print('fetch success')
@ -151,9 +162,7 @@ class AssetGather:
def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
"""Fetch a given url to a given filename for a given AssetGather.
This """
"""Fetch a given url to a given filename."""
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
# the maximum. Perhaps we should aim for a few cycles per second on
# an average connection?..
block_sz = 1024 * 100 * 2
block_sz = 1024 * 1024
time_outs = 0
while True:
try:
data = ureq.read(block_sz)
except socket.timeout:
# File has not had activity in max seconds.
if time_outs > 3:
print("\n\n\nsorry -- try back later")

View File

@ -1,6 +1,6 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<!--DOCSHASH=5b1863340d0e423b597b188ee7c7ac23-->
<h4><em>last updated on 2020-03-11 for Ballistica version 1.5.0 build 20001</em></h4>
<!--DOCSHASH=0ccb27f0a14c6ad6f07acd13b8bd2fbb-->
<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,
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>

View File

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