Fix T49430: append scene with gamelogic broken.
authorBastien Montagne <montagne29@wanadoo.fr>
Fri, 23 Sep 2016 11:05:11 +0000 (13:05 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Fri, 23 Sep 2016 12:25:15 +0000 (14:25 +0200)
In fact, it was the whole remapping process that was broken in logic bricks area,
due to terrible design of links between those bricks...

Object copying was also broken in that case, fixed as well.

To be backported to 2.78.

Note that issue was actually probably there since ages, hidden behind dirty hacks
used in previous append code (though likely visible in some corner cases).

Listen kids: do not, never, ever, do what has been done for links between logic bricks. Never. Ever.
Even as pure runtime data it would have been bad, but as stored data...

source/blender/blenkernel/BKE_sca.h
source/blender/blenkernel/intern/library_remap.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/sca.c

index 1743a4431fd93cc9416ce7d1bcac89186a1389c4..a504f1bac3d97210f2e200582d2a709686c160f8 100644 (file)
@@ -31,8 +31,9 @@
  *  \ingroup bke
  */
 
-struct bSensor;
+struct Main;
 struct Object;
+struct bSensor;
 struct bController;
 struct bActuator;
 
@@ -68,6 +69,9 @@ void clear_sca_new_poins(void);
 void set_sca_new_poins_ob(struct Object *ob);
 void set_sca_new_poins(void);
 
+void BKE_sca_logic_links_remap(struct Main *bmain, struct Object *ob_old, struct Object *ob_new);
+void BKE_sca_logic_copy(struct Object *ob_new, struct Object *ob);
+
 void sca_move_sensor(struct bSensor *sens_to_move, struct Object *ob, int move_up);
 void sca_move_controller(struct bController *cont_to_move, struct Object *ob, int move_up);
 void sca_move_actuator(struct bActuator *act_to_move, struct Object *ob, int move_up);
index f45636493029278b7e6253cc801fea07b309a280..b468e6436c868d91ed381f48bb5d3aa2549c2a04 100644 (file)
@@ -99,6 +99,7 @@
 #include "BKE_object.h"
 #include "BKE_paint.h"
 #include "BKE_particle.h"
+#include "BKE_sca.h"
 #include "BKE_speaker.h"
 #include "BKE_sound.h"
 #include "BKE_screen.h"
@@ -448,6 +449,10 @@ ATTR_NONNULL(1) static void libblock_remap_data(
                }
        }
 
+       if (old_id && GS(old_id->name) == ID_OB) {
+               BKE_sca_logic_links_remap(bmain, (Object *)old_id, (Object *)new_id);
+       }
+
        /* XXX We may not want to always 'transfer' fakeuser from old to new id... Think for now it's desired behavior
         *     though, we can always add an option (flag) to control this later if needed. */
        if (old_id && (old_id->flag & LIB_FAKEUSER)) {
index cdd7560e3c43c733fe7525c6c8d9dea2ef4540c6..d87c257d555b01073b1381e5d32f60723b8bbca9 100644 (file)
@@ -1118,11 +1118,9 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
 
        BLI_listbase_clear(&obn->prop);
        BKE_bproperty_copy_list(&obn->prop, &ob->prop);
-       
-       copy_sensors(&obn->sensors, &ob->sensors);
-       copy_controllers(&obn->controllers, &ob->controllers);
-       copy_actuators(&obn->actuators, &ob->actuators);
-       
+
+       BKE_sca_logic_copy(obn, ob);
+
        if (ob->pose) {
                copy_object_pose(obn, ob);
                /* backwards compat... non-armatures can get poses in older files? */
index a468420f87dd44f9e5da6864d373f155c697c811..c357b2ef9f76773cd5807cbe55d8cc412282e942 100644 (file)
@@ -44,7 +44,9 @@
 #include "DNA_object_types.h"
 
 #include "BLI_blenlib.h"
+#include "BLI_ghash.h"
 #include "BLI_math.h"
+
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BKE_library.h"
@@ -653,6 +655,160 @@ void set_sca_new_poins(void)
        }
 }
 
+/**
+ * Try to remap logic links to new object... Very, *very* weak.
+ */
+/* XXX Logick bricks... I don't have words to say what I think about this behavior.
+ *     They have silent hidden ugly inter-objects dependencies (a sensor can link into any other
+ *     object's controllers, and same between controllers and actuators, without *any* explicit reference
+ *     to data-block involved).
+ *     This is bad, bad, bad!!!
+ *     ...and forces us to add yet another very ugly hack to get remapping with logic bricks working. */
+void BKE_sca_logic_links_remap(Main *bmain, Object *ob_old, Object *ob_new)
+{
+       GHash *controllers_map = ob_old->controllers.first ?
+                                    BLI_ghash_ptr_new_ex(__func__, BLI_listbase_count(&ob_old->controllers)) : NULL;
+       GHash *actuators_map = ob_old->actuators.first ?
+                                  BLI_ghash_ptr_new_ex(__func__, BLI_listbase_count(&ob_old->actuators)) : NULL;
+
+       if (!(controllers_map || actuators_map)) {
+               return;
+       }
+
+       /* We try to remap old controllers/actuators to new ones - in a very basic way. */
+       for (bController *cont_old = ob_old->controllers.first, *cont_new = ob_new->controllers.first;
+            cont_old;
+            cont_old = cont_old->next)
+       {
+               bController *cont_new2 = cont_new;
+
+               if (cont_old->mynew != NULL) {
+                       cont_new2 = cont_old->mynew;
+                       if (!(cont_new2 == cont_new || BLI_findindex(&ob_new->controllers, cont_new2) >= 0)) {
+                               cont_new2 = NULL;
+                       }
+               }
+               else if (cont_new && cont_old->type != cont_new->type) {
+                       cont_new2 = NULL;
+               }
+
+               BLI_ghash_insert(controllers_map, cont_old, cont_new2);
+
+               if (cont_new) {
+                       cont_new = cont_new->next;
+               }
+       }
+
+       for (bActuator *act_old = ob_old->actuators.first, *act_new = ob_new->actuators.first;
+            act_old;
+            act_old = act_old->next)
+       {
+               bActuator *act_new2 = act_new;
+
+               if (act_old->mynew != NULL) {
+                       act_new2 = act_old->mynew;
+                       if (!(act_new2 == act_new || BLI_findindex(&ob_new->actuators, act_new2) >= 0)) {
+                               act_new2 = NULL;
+                       }
+               }
+               else if (act_new && act_old->type != act_new->type) {
+                       act_new2 = NULL;
+               }
+
+               BLI_ghash_insert(actuators_map, act_old, act_new2);
+
+               if (act_new) {
+                       act_new = act_new->next;
+               }
+       }
+
+       for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
+               if (controllers_map != NULL) {
+                       for (bSensor *sens = ob->sensors.first; sens; sens = sens->next) {
+                               for (int a = 0; a < sens->totlinks; a++) {
+                                       if (sens->links[a]) {
+                                               bController *old_link = sens->links[a];
+                                               bController **new_link_p = (bController **)BLI_ghash_lookup_p(controllers_map, old_link);
+
+                                               if (new_link_p == NULL) {
+                                                       /* old_link is *not* in map's keys (i.e. not to any ob_old->controllers),
+                                                        * which means we ignore it totally here. */
+                                               }
+                                               else if (*new_link_p == NULL) {
+                                                       unlink_logicbricks((void **)&old_link, (void ***)&(sens->links), &sens->totlinks);
+                                                       a--;
+                                               }
+                                               else {
+                                                       sens->links[a] = *new_link_p;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               if (actuators_map != NULL) {
+                       for (bController *cont = ob->controllers.first; cont; cont = cont->next) {
+                               for (int a = 0; a < cont->totlinks; a++) {
+                                       if (cont->links[a]) {
+                                               bActuator *old_link = cont->links[a];
+                                               bActuator **new_link_p = (bActuator **)BLI_ghash_lookup_p(actuators_map, old_link);
+
+                                               if (new_link_p == NULL) {
+                                                       /* old_link is *not* in map's keys (i.e. not to any ob_old->actuators),
+                                                        * which means we ignore it totally here. */
+                                               }
+                                               else if (*new_link_p == NULL) {
+                                                       unlink_logicbricks((void **)&old_link, (void ***)&(cont->links), &cont->totlinks);
+                                                       a--;
+                                               }
+                                               else {
+                                                       cont->links[a] = *new_link_p;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (controllers_map) {
+               BLI_ghash_free(controllers_map, NULL, NULL);
+       }
+       if (actuators_map) {
+               BLI_ghash_free(actuators_map, NULL, NULL);
+       }
+}
+
+/**
+ * Handle the copying of logic data into a new object, including internal logic links update.
+ * External links (links between logic bricks of different objects) must be handled separately.
+ */
+void BKE_sca_logic_copy(Object *ob_new, Object *ob)
+{
+       copy_sensors(&ob_new->sensors, &ob->sensors);
+       copy_controllers(&ob_new->controllers, &ob->controllers);
+       copy_actuators(&ob_new->actuators, &ob->actuators);
+
+       for (bSensor *sens = ob->sensors.first; sens; sens = sens->next) {
+               if (sens->flag & SENS_NEW) {
+                       for (int a = 0; a < sens->totlinks; a++) {
+                               if (sens->links[a] && sens->links[a]->mynew) {
+                                       sens->links[a] = sens->links[a]->mynew;
+                               }
+                       }
+               }
+       }
+
+       for (bController *cont = ob->controllers.first; cont; cont = cont->next) {
+               if (cont->flag & CONT_NEW) {
+                       for (int a = 0; a < cont->totlinks; a++) {
+                               if (cont->links[a] && cont->links[a]->mynew) {
+                                       cont->links[a] = cont->links[a]->mynew;
+                               }
+                       }
+               }
+       }
+}
+
 /* ******************** INTERFACE ******************* */
 void sca_move_sensor(bSensor *sens_to_move, Object *ob, int move_up)
 {