diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index e1d921e1..11ef065c 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -95,6 +95,7 @@ assetpath assettype assettypestr + astc astcenc astroid asus @@ -553,6 +554,7 @@ fieldtypes filebytes filecmp + fileext filehash fileinfo fileinput diff --git a/assets/.asset_manifest_1.json b/assets/.asset_manifest_1.json index 655304cf..fb37d9d1 100644 --- a/assets/.asset_manifest_1.json +++ b/assets/.asset_manifest_1.json @@ -101,11 +101,13 @@ "data/scripts/ba/ui/__pycache__/__init__.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__init__.py", "data/scripts/bafoundation/__pycache__/__init__.cpython-37.opt-1.pyc", + "data/scripts/bafoundation/__pycache__/assets.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__pycache__/dataclassutils.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__pycache__/err.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__pycache__/executils.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__pycache__/jsonutils.cpython-37.opt-1.pyc", "data/scripts/bafoundation/__pycache__/util.cpython-37.opt-1.pyc", + "data/scripts/bafoundation/assets.py", "data/scripts/bafoundation/dataclassutils.py", "data/scripts/bafoundation/entity/__init__.py", "data/scripts/bafoundation/entity/__pycache__/__init__.cpython-37.opt-1.pyc", diff --git a/assets/Makefile b/assets/Makefile index 30647a4b..0496e17c 100644 --- a/assets/Makefile +++ b/assets/Makefile @@ -148,6 +148,7 @@ SCRIPT_TARGETS_PY_1 = \ build/data/scripts/bafoundation/dataclassutils.py \ build/data/scripts/bafoundation/util.py \ build/data/scripts/bafoundation/__init__.py \ + build/data/scripts/bafoundation/assets.py \ build/data/scripts/bafoundation/jsonutils.py \ build/data/scripts/bafoundation/err.py \ build/data/scripts/bafoundation/entity/_base.py \ @@ -383,6 +384,7 @@ SCRIPT_TARGETS_PYC_1 = \ build/data/scripts/bafoundation/__pycache__/dataclassutils.cpython-37.opt-1.pyc \ build/data/scripts/bafoundation/__pycache__/util.cpython-37.opt-1.pyc \ build/data/scripts/bafoundation/__pycache__/__init__.cpython-37.opt-1.pyc \ + build/data/scripts/bafoundation/__pycache__/assets.cpython-37.opt-1.pyc \ build/data/scripts/bafoundation/__pycache__/jsonutils.cpython-37.opt-1.pyc \ build/data/scripts/bafoundation/__pycache__/err.cpython-37.opt-1.pyc \ build/data/scripts/bafoundation/entity/__pycache__/_base.cpython-37.opt-1.pyc \ @@ -654,6 +656,11 @@ build/data/scripts/bafoundation/__pycache__/__init__.cpython-37.opt-1.pyc: \ @echo Compiling script: $^ @rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@ +build/data/scripts/bafoundation/__pycache__/assets.cpython-37.opt-1.pyc: \ + build/data/scripts/bafoundation/assets.py + @echo Compiling script: $^ + @rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@ + build/data/scripts/bafoundation/__pycache__/jsonutils.cpython-37.opt-1.pyc: \ build/data/scripts/bafoundation/jsonutils.py @echo Compiling script: $^ diff --git a/assets/src/data/scripts/ba/_dependency.py b/assets/src/data/scripts/ba/_dependency.py index 9e4a45f3..c4ed9fbf 100644 --- a/assets/src/data/scripts/ba/_dependency.py +++ b/assets/src/data/scripts/ba/_dependency.py @@ -26,7 +26,6 @@ import weakref from typing import (Generic, TypeVar, TYPE_CHECKING) import _ba -from ba import _general if TYPE_CHECKING: from typing import Optional, Any, Dict, List, Set, Type @@ -63,8 +62,9 @@ class Dependency(Generic[T]): def get_hash(self) -> int: """Return the dependency's hash, calculating it if necessary.""" + from bafoundation.util import make_hash if self._hash is None: - self._hash = _general.make_hash((self.cls, self.config)) + self._hash = make_hash((self.cls, self.config)) return self._hash def __get__(self, obj: Any, cls: Any = None) -> T: diff --git a/assets/src/data/scripts/ba/_general.py b/assets/src/data/scripts/ba/_general.py index aee69116..a1900b22 100644 --- a/assets/src/data/scripts/ba/_general.py +++ b/assets/src/data/scripts/ba/_general.py @@ -21,7 +21,6 @@ """Utility snippets applying to generic Python code.""" from __future__ import annotations -import copy import types import weakref from typing import TYPE_CHECKING, TypeVar @@ -261,21 +260,3 @@ class WeakMethod: def __str__(self) -> str: return '' - - -def make_hash(obj: Any) -> int: - """Makes a hash from a dictionary, list, tuple or set to any level, - that contains only other hashable types (including any lists, tuples, - sets, and dictionaries). - """ - - if isinstance(obj, (set, tuple, list)): - return hash(tuple([make_hash(e) for e in obj])) - if not isinstance(obj, dict): - return hash(obj) - - new_obj = copy.deepcopy(obj) - for k, v in new_obj.items(): - new_obj[k] = make_hash(v) - - return hash(tuple(frozenset(sorted(new_obj.items())))) diff --git a/assets/src/data/scripts/bafoundation/assets.py b/assets/src/data/scripts/bafoundation/assets.py new file mode 100644 index 00000000..cad807ed --- /dev/null +++ b/assets/src/data/scripts/bafoundation/assets.py @@ -0,0 +1,69 @@ +# Copyright (c) 2011-2019 Eric Froemling +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ----------------------------------------------------------------------------- +"""Functionality related to cloud based assets.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING +from enum import Enum + +from bafoundation import entity + +if TYPE_CHECKING: + pass + + +class AssetPackageFlavor(Enum): + """Flavors for asset package outputs for different platforms/etc.""" + + # DXT3/DXT5 textures + DESKTOP = 'desktop' + + # ASTC textures + MOBILE = 'mobile' + + +class AssetType(Enum): + """Types for individual assets within a package.""" + TEXTURE = 'texture' + SOUND = 'sound' + DATA = 'data' + + +class AssetInfo(entity.CompoundValue): + """Info for a specific asset file in a package.""" + filehash = entity.Field('h', entity.StringValue()) + fileext = entity.Field('e', entity.StringValue()) + + +class AssetPackageFlavorManifestValue(entity.CompoundValue): + """A manifest of asset info for a specific flavor of an asset package.""" + assets = entity.CompoundDictField('a', str, AssetInfo()) + + +class AssetPackageFlavorManifest(entity.EntityMixin, + AssetPackageFlavorManifestValue): + """A self contained AssetPackageFlavorManifestValue.""" + + +class AssetPackageBuildState(entity.Entity): + """Contains info about an in-progress asset cloud build.""" + in_progress_builds = entity.ListField('b', entity.StringValue()) diff --git a/assets/src/data/scripts/bafoundation/util.py b/assets/src/data/scripts/bafoundation/util.py index eadd647d..a769d6d6 100644 --- a/assets/src/data/scripts/bafoundation/util.py +++ b/assets/src/data/scripts/bafoundation/util.py @@ -279,3 +279,25 @@ class ValueDispatcher1Arg(Generic[TVAL, TARG, TRET]): """Add a handler to the dispatcher.""" from functools import partial return partial(self._add_handler, value) + + +def make_hash(obj: Any) -> int: + """Makes a hash from a dictionary, list, tuple or set to any level, + that contains only other hashable types (including any lists, tuples, + sets, and dictionaries). + + Note that this uses Python's hash() function internally so collisions/etc. + may be more common than with fancy cryptographic hashes. + """ + import copy + + if isinstance(obj, (set, tuple, list)): + return hash(tuple([make_hash(e) for e in obj])) + if not isinstance(obj, dict): + return hash(obj) + + new_obj = copy.deepcopy(obj) + for k, v in new_obj.items(): + new_obj[k] = make_hash(v) + + return hash(tuple(frozenset(sorted(new_obj.items())))) diff --git a/docs/ba_module.md b/docs/ba_module.md index a7f47852..e9aa0fce 100644 --- a/docs/ba_module.md +++ b/docs/ba_module.md @@ -1,6 +1,6 @@ - -

last updated on 2020-01-17 for Ballistica version 1.5.0 build 20001

+ +

last updated on 2020-01-23 for Ballistica version 1.5.0 build 20001

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 let me know. Happy modding!


diff --git a/tests/test_bafoundation/test_entities.py b/tests/test_bafoundation/test_entities.py index a97eeec8..b0c44e53 100644 --- a/tests/test_bafoundation/test_entities.py +++ b/tests/test_bafoundation/test_entities.py @@ -297,6 +297,37 @@ def test_entity_mixin() -> None: assert ent.isubval2 == 3453 +def test_entity_embedding() -> None: + """Making sure compound entities work as expected.""" + + class EmbCompoundValTest(entity.CompoundValue): + """Testing...""" + isubval = entity.Field('i', entity.IntValue(default=12345)) + + class EmbCompoundTest(entity.Entity): + """Testing...""" + isubval = entity.Field('i', entity.IntValue(default=12345)) + sub = entity.CompoundField('sub', EmbCompoundValTest()) + + # This should be ok... + _ent = EmbCompoundTest() + + class EmbCompoundValTest2(entity.Entity): + """Testing...""" + isubval = entity.Field('i', entity.IntValue(default=12345)) + + with pytest.raises(AssertionError): + + # This should not be ok + # (can only embed CompoundValues, not complete Entities) + class EmbCompoundTest2(entity.Entity): + """Testing...""" + isubval = entity.Field('i', entity.IntValue(default=12345)) + sub = entity.CompoundField('sub', EmbCompoundValTest2()) + + _ent2 = EmbCompoundTest2() + + def test_key_uniqueness() -> None: """Make sure entities reject multiple fields with the same key.""" diff --git a/tools/snippets b/tools/snippets index b4e23adc..28abbe5c 100755 --- a/tools/snippets +++ b/tools/snippets @@ -670,8 +670,9 @@ def update_docs_md() -> None: pysources = [] exts = ['.cc', '.c', '.h', '.py'] for basedir in [ - 'src/ballistica/python', 'assets/src/data/scripts/bafoundation', - 'assets/src/data/scripts/ba' + 'src/ballistica/python', + 'assets/src/data/scripts/bafoundation', + 'assets/src/data/scripts/ba', ]: assert os.path.isdir(basedir) for root, _dirs, files in os.walk(basedir):