Compare commits

..

4 Commits

Author SHA1 Message Date
Adam Johnson eb250b0ece
Bump B4K to include refcounting fix. 3 months ago
Adam Johnson 7f43de5896 Allow Python File nodes to activate each other. 3 months ago
Adam Johnson 83ada7cbf8
Merge pull request #432 from Hoikas/fix_423 3 months ago
Adam Johnson 9b7fbb7c76
Add mitigations for #423. 5 months ago
  1. 4
      .github/workflows/ci_build.yml
  2. 4
      korman/exporter/camera.py
  3. 2
      korman/exporter/material.py
  4. 44
      korman/nodes/node_python.py

4
.github/workflows/ci_build.yml

@ -12,7 +12,7 @@ jobs:
cmake-arch: Win32, cmake-arch: Win32,
python-arch: x86, python-arch: x86,
str: windows-x86, str: windows-x86,
blender-url: "https://github.com/Hoikas/blender2.7/releases/download/blender2.79_20221113/blender-2.79.0-git20221114.f970f178c093-windows32.zip", blender-url: "https://github.com/Hoikas/blender2.7/releases/download/blender2.79_20250208/blender-2.79.0-git20250208.106afc6db53a-windows32.zip",
} }
- { - {
os: windows-2022, os: windows-2022,
@ -20,7 +20,7 @@ jobs:
cmake-arch: x64, cmake-arch: x64,
python-arch: x64, python-arch: x64,
str: windows-x64, str: windows-x64,
blender-url: "https://github.com/Hoikas/blender2.7/releases/download/blender2.79_20221113/blender-2.79.0-git20221114.f970f178c093-windows64.zip", blender-url: "https://github.com/Hoikas/blender2.7/releases/download/blender2.79_20250208/blender-2.79.0-git20250208.106afc6db53a-windows64.zip",
} }
env: env:

4
korman/exporter/camera.py

@ -97,6 +97,10 @@ class CameraConverter:
continue continue
cam_trans = plCameraModifier.CamTrans() cam_trans = plCameraModifier.CamTrans()
if manual_trans.camera: if manual_trans.camera:
# Don't even bother if a disabled camera is referenced. If we export camera modifier
# for a disabled camera, then it won't get a brain, and the client will crash.
if not manual_trans.camera.plasma_object.enabled:
continue
cam_trans.transTo = self._mgr.find_create_key(plCameraModifier, bl=manual_trans.camera) cam_trans.transTo = self._mgr.find_create_key(plCameraModifier, bl=manual_trans.camera)
cam_trans.ignore = manual_trans.mode == "ignore" cam_trans.ignore = manual_trans.mode == "ignore"

2
korman/exporter/material.py

@ -770,6 +770,8 @@ class MaterialConverter:
# least a SceneObject and CoordInterface so that we can touch it... # least a SceneObject and CoordInterface so that we can touch it...
# NOTE: that harvest_actor makes sure everyone alread knows we're going to have a CI # NOTE: that harvest_actor makes sure everyone alread knows we're going to have a CI
if isinstance(viewpt.data, bpy.types.Camera): if isinstance(viewpt.data, bpy.types.Camera):
if not viewpt.plasma_object.enabled:
raise ExportError(f"DynamicCamMap '{texture.name} wants to use the camera '{viewpt.name}', but it is not a Plasma Object!")
pl_env.camera = self._mgr.find_create_key(plCameraModifier, bl=viewpt) pl_env.camera = self._mgr.find_create_key(plCameraModifier, bl=viewpt)
else: else:
pl_env.rootNode = self._mgr.find_create_key(plSceneObject, bl=viewpt) pl_env.rootNode = self._mgr.find_create_key(plSceneObject, bl=viewpt)

44
korman/nodes/node_python.py

@ -13,12 +13,16 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
from bpy.props import * from bpy.props import *
from collections.abc import Iterable from collections.abc import Iterable
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import *
from PyHSPlasma import * from PyHSPlasma import *
from .. import enum_props from .. import enum_props
@ -68,9 +72,12 @@ _attrib2param = {
_attrib_key_types = { _attrib_key_types = {
"ptAttribSceneobject": plFactory.ClassIndex("plSceneObject"), "ptAttribSceneobject": plFactory.ClassIndex("plSceneObject"),
"ptAttribSceneobjectList": plFactory.ClassIndex("plSceneObject"), "ptAttribSceneobjectList": plFactory.ClassIndex("plSceneObject"),
"ptAttribActivator": plFactory.ClassIndex("plLogicModifier"), "ptAttribActivator": (plFactory.ClassIndex("plLogicModifier"),
"ptAttribActivatorList": plFactory.ClassIndex("plLogicModifier"), plFactory.ClassIndex("plPythonFileMod")),
"ptAttribNamedActivator": plFactory.ClassIndex("plLogicModifier"), "ptAttribActivatorList": (plFactory.ClassIndex("plLogicModifier"),
plFactory.ClassIndex("plPythonFileMod")),
"ptAttribNamedActivator": (plFactory.ClassIndex("plLogicModifier"),
plFactory.ClassIndex("plPythonFileMod")),
"ptAttribResponder": plFactory.ClassIndex("plResponderModifier"), "ptAttribResponder": plFactory.ClassIndex("plResponderModifier"),
"ptAttribResponderList": plFactory.ClassIndex("plResponderModifier"), "ptAttribResponderList": plFactory.ClassIndex("plResponderModifier"),
"ptAttribNamedResponder": plFactory.ClassIndex("plResponderModifier"), "ptAttribNamedResponder": plFactory.ClassIndex("plResponderModifier"),
@ -187,6 +194,17 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
bl_label = "Python File" bl_label = "Python File"
bl_width_default = 290 bl_width_default = 290
# Yas, a PythonFileMod can activate another PythonFileMod
pl_attrib = {"ptAttribActivator", "ptAttribActivatorList", "ptAttribNamedActivator"}
output_sockets: dict[str, dict[str, Any]] = {
"satisfies": {
"text": "Satisfies",
"type": "PlasmaConditionSocket",
"valid_link_nodes": "PlasmaPythonFileNode",
},
}
def _poll_pytext(self, value): def _poll_pytext(self, value):
return value.name.endswith(".py") return value.name.endswith(".py")
@ -304,7 +322,7 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
param.value = i param.value = i
if not socket.is_simple_value: if not socket.is_simple_value:
self._export_key_attrib(exporter, bo, so, i, socket) self._export_key_attrib(exporter, bo, so, pfm, i, socket)
pfm.addParameter(param) pfm.addParameter(param)
def _export_ancillary_sceneobject(self, exporter, bo, so: plSceneObject) -> None: def _export_ancillary_sceneobject(self, exporter, bo, so: plSceneObject) -> None:
@ -317,7 +335,7 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
exporter.report.msg(f"Marking RT light '{so.key.name}' as animated due to usage in a Python File node", so.key.name) exporter.report.msg(f"Marking RT light '{so.key.name}' as animated due to usage in a Python File node", so.key.name)
light.setProperty(plLightInfo.kLPMovable, True) light.setProperty(plLightInfo.kLPMovable, True)
def _export_key_attrib(self, exporter, bo, so : plSceneObject, key : plKey, socket) -> None: def _export_key_attrib(self, exporter, bo, so: plSceneObject, pfm: plPythonFileMod, key: plKey, socket) -> None:
if key is None: if key is None:
exporter.report.warn("Attribute '{}' didn't return a key and therefore will be unavailable to Python", exporter.report.warn("Attribute '{}' didn't return a key and therefore will be unavailable to Python",
self.id_data.name, socket.links[0].name) self.id_data.name, socket.links[0].name)
@ -333,8 +351,11 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
self.id_data.name, socket.links[0].from_node.name, self.id_data.name, socket.links[0].from_node.name,
plFactory.ClassName(key.type)) plFactory.ClassName(key.type))
if isinstance(key.object, plSceneObject): key_object = key.object
self._export_ancillary_sceneobject(exporter, bo, key.object) if isinstance(key_object, plSceneObject):
self._export_ancillary_sceneobject(exporter, bo, key_object)
elif isinstance(key_object, plPythonFileMod):
key_object.addReceiver(pfm.key)
def _get_attrib_sockets(self, idx): def _get_attrib_sockets(self, idx):
for i in self.inputs: for i in self.inputs:
@ -342,8 +363,9 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
yield i yield i
def generate_valid_links_for(self, context, socket, is_output): def generate_valid_links_for(self, context, socket, is_output):
# Python nodes have no outputs... if is_output:
assert is_output is False yield from PlasmaNodeBase.generate_valid_links_for(self, context, socket, True)
return
attrib_type = socket.attribute_type attrib_type = socket.attribute_type
for i in bpy.types.Node.__subclasses__(): for i in bpy.types.Node.__subclasses__():
@ -493,6 +515,10 @@ class PlasmaPythonFileNode(PlasmaVersionedNode, bpy.types.Node):
while len(unconnected) > 1: while len(unconnected) > 1:
self.inputs.remove(unconnected.pop()) self.inputs.remove(unconnected.pop())
# Make sure the output sockets are present and accounted for.
self._update_extant_sockets(self.output_sockets, self.outputs)
self._update_init_sockets(self.output_sockets, self.outputs)
@property @property
def latest_version(self): def latest_version(self):
return 2 return 2

Loading…
Cancel
Save