Scaling Scene Props In Multiplayer

Users who are viewing this thread

Hello fellow modders, I have been modding for some quite time and I haven't able to solve this issue. Issue is when I scale a scene prop in multiplayer via scripts after mission started their textures doesn't scale and physics of the object get messy. Question is how can I prevent physics get messy and how can I make textures scale too? Here is my code of work for that you can test it yourself on a dedicated server. Thanks in advance.

module_scripts.py:
Code:
("spawn_boundaries",
    [
        (init_position, pos0),
        (get_scene_boundaries, pos1, pos2),
        (position_get_x, ":pos1x",pos1),
        (position_get_x, ":x", pos2),
        (val_add, ":x", ":pos1x"),
        (val_div, ":x", 2),
        (position_get_x, ":pos1y",pos1),
        (position_get_y, ":y", pos2),
        (val_add, ":y", ":pos1y"),
        (val_div, ":y", 2),

        (try_for_range, ":count", 0, 4),
            (try_begin),
                (eq, ":count", 0), #north
                (store_mul, ":newy", ":y", 2),
                (store_div, ":minusy", ":newy", 8),
                (val_sub, ":newy", ":minusy"),
                (position_set_x, pos0, ":x"),
                (position_set_y, pos0, ":newy"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_x, reg1, pos1),
                (val_mul, reg1, 100),
                (position_get_y, reg2, pos1),
                (position_get_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_x, pos1, reg1),
                (position_set_scale_z, pos1, reg3),
                (prop_instance_set_scale, reg0, pos1, pos1, pos1),
                (prop_instance_set_position, reg0, pos0),
            (else_try),
                (eq, ":count", 1), #east
                (store_mul, ":newx", ":x", 2),
                (store_div, ":minusx", ":newx", 8),
                (val_sub, ":newx", ":minusx"),
                (position_set_x, pos0, ":newx"),
                (position_set_y, pos0, ":y"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_x, reg1, pos1),
                (position_get_y, reg2, pos1),
                (val_mul, reg2, 100),
                (position_get_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_y, pos1, reg2),
                (position_set_scale_z, pos1, reg3),
                (prop_instance_set_scale, reg0, pos1, pos1, pos1),
                (prop_instance_set_position, reg0, pos0),
            (else_try),
                (eq, ":count", 2), #south
                (store_mul, ":newy", ":y", 2),
                (val_div, ":newy", 16),
                (position_set_x, pos0, ":x"),
                (position_set_y, pos0, ":newy"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_x, reg1, pos1),
                (val_mul, reg1, 100),
                (position_get_y, reg2, pos1),
                (position_get_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_x, pos1, reg1),
                (position_set_scale_z, pos1, reg3),
                (prop_instance_set_scale, reg0, pos1, pos1, pos1),
                (prop_instance_set_position, reg0, pos0),
            (else_try),
                (eq, ":count", 3), #west
                (store_mul, ":newx", ":x", 2),
                (val_div, ":newx", 16),
                (position_set_x, pos0, ":newx"),
                (position_set_y, pos0, ":y"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_x, reg1, pos1),
                (position_get_y, reg2, pos1),
                (val_mul, reg2, 100),
                (position_get_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_y, pos1, reg2),
                (position_set_scale_z, pos1, reg3),
                (prop_instance_set_scale, reg0, pos1, pos1, pos1),
                (prop_instance_set_position, reg0, pos0),
            (try_end),
        (try_end),
    ]),

module_mission_templates.py
Code:
(ti_after_mission_start, 0, 0, [],
[
    (try_begin),
        (multiplayer_is_server),
        (call_script, "script_spawn_skyboxes"),
        (call_script, "script_spawn_boundaries"),
    (try_end),

    (try_begin),
        (neg^multiplayer_is_server),
    (try_end),
])

module_scene_props.py
Code:
boundary_triggers = [
  (ti_on_scene_prop_use, [
    (store_trigger_param_1, ":agent_id"),
    (store_trigger_param_2, ":instance"),
    (multiplayer_send_int_to_server, client_event_migration, ":agent_id"),
  ]),
]
scene_props = [
  # native skyboxes
  ("skybox_cloudy_day", 0, "skybox_cloud_1", "0", skybox_triggers),
  ("skybox_rainy_day", 0, "skybox_cloud_2", "0", skybox_triggers),
  ("skybox_clear_day", 0, "skybox_clearday", "0", skybox_triggers),
  ("skybox_clear_night", 0, "skybox_night_1", "0", skybox_triggers),
  ("skybox_cloudy_night", 0, "skybox_night_2", "0", skybox_triggers),
  ("skybox_sunset", 0, "skybox_sunset_1", "0", skybox_triggers),
  # native skyboxes
  #("barrier_20m", 0, "barrier_20m", "bo_barrier_20m", boundary_triggers),
  ("barrier_20m", sokf_moveable, "barrier_20m", "bo_barrier_20m", boundary_triggers),
]
])
 
Last edited:
A few things:
  1. make sure not to make typos (^) such as in neg^multiplayer_is_server,
  2. you can insert the server check command in the first square bracket field such as in 0, 0, [],
  3. I am not sure at all, but you may try using the rebuild_shadow_map operation since it also rebuilds collision (apparently). I must stress I am not sure if that is the case; I only remember it from Dalion's message on Discord many months ago, so I may be wrong,
  4. I also am not sure if your ti_after_mission_start trigger type is the right choice for what you want to achieve. After the initialization of props it may not be possible to easily spawn, modify and use scripted properties of new ones without introducing some server/ client network events,
  5. on a side note, skybox can be set with set_skybox so I do not know how you want to spawn them.
 
Upvote 0
A few things:
  1. make sure not to make typos (^) such as in neg^multiplayer_is_server,
  2. you can insert the server check command in the first square bracket field such as in 0, 0, [],
  3. I am not sure at all, but you may try using the rebuild_shadow_map operation since it also rebuilds collision (apparently). I must stress I am not sure if that is the case; I only remember it from Dalion's message on Discord many months ago, so I may be wrong,
  4. I also am not sure if your ti_after_mission_start trigger type is the right choice for what you want to achieve. After the initialization of props it may not be possible to easily spawn, modify and use scripted properties of new ones without introducing some server/ client network events,
  5. on a side note, skybox can be set with set_skybox so I do not know how you want to spawn them.
1. Thank you for your reply but '^' is not a typo its xor and as far as i know its more precise when it comes to negations.
2. Thanks will try that.
3. If this works i will let you know.
4. You can't spawn it before mission starts
5. I have been updating the time dynamically and set skybox is one time for using so i have to use skyboxes as scene props.

EDIT: It didn't work when i execute rebuild shadow map operation, results are the same.
 
Upvote 0
In Warband I only know of the negation via neg|<operation>. I wasn't aware of that either and it seems to work as he does not get any error at compiling. I would however rather not drop it at the modding guide because I don't think it brings any additional advantage at MaBL compared to the regular negation, it should have the same effect (to make sure of that I would however test the whole thing also with a regular |).
 
Upvote 1
I have not ever learned about ^, so perhaps it can be mentioned in the Modding Guide. As for the rest, it depends on what you are modding? NW, PK or Native?
No our own module from scrap.
In Warband I only know of the negation via neg|<operation>. I wasn't aware of that either and it seems to work as he does not get any error at compiling. I would however rather not drop it at the modding guide because I don't think it brings any additional advantage at MaBL compared to the regular negation, it should have the same effect (to make sure of that I would however test the whole thing also with a regular |).
Check bitshifting tutorials on the internet. OR operation makes some values missing in some big numbers and XOR represents them better. Only difference between them is precise things. There is no performance or functionality differences.
 
Last edited:
Upvote 0
Hi, Eärendil brought me here. The neg^multiplayer_is_server thing is very misleading. This is processed by the Python-based compiler when generating the weird lines that end up in the .txt files and it only works at all because the bits of both numbers don't touch.

Python:
>>> hex(0x80000000 | 417)
'0x800001a1'
>>> hex(0x80000000 ^ 417)
'0x800001a1'

The game sees the same M&B operation code / opcode. So what? Why? ¯\_(ツ)_/¯

An exclusive OR (XOR) works like a plain OR whenever both bits are not one, see the truth table here and compare XOR with OR. So using this for flags is a bit like falling sideways while going down a rail but sticking the landing; ^ technically produces the same result, but when you want to combine flags in a bitfield with the rest of a number you really want to use the | operation to merge all the 1 bits from top and bottom into the same number, you don't really want that your bits return zero whenever the top and bottom bits are one, even if in this case does not happen, so you don't get this unintentional behavior with these particular module system numbers.

And I don't want to sound mean, but don't think the precision argument has any merit in this case because you are not shifting/rotating bits left or right, they stay in place, and they are of the same type/size. Sounds a bit like programming snake oil.

Python:
10000000000000000000000000000000 # 0x80000000 => 2147483648 in decimal => neg
                       110100001 #      0x1a1 =>        417 in decimal => multiplayer_is_server
-------------------------------- # ----------
10000000000000000000000110100001 # 0x800001a1 = (0x80000000 | 0x1a1) which is the same as doing (0x80000000 | 417)


In the table above, if you do an OR operation, as long as there is some 1 in the input (e.g. 1|1, 0|1, 1|0) it will output one, for XOR if both are one (1^1) will be zero, this is generally used to negate or toggle boolean variables easily. Turning True into False and vice versa.

TL;DR: Don't get me wrong, XOR is very useful for many other low-level and fun operations, but it shouldn't be used for concatenating flags. I can use other unrelated operations that also give the same result and look cool, but that doesn't make it right.

Python:
>>> hex(0x80000000 + 417)
'0x800001a1'
>>> hex((0x80000000 - ~417) - 1)
'0x800001a1'
>>> hex(~(~0x80000000 & ~417)) # swy: same as OR, but obfuscated with NAND
'0x800001a1'

Hope that helps at least a bit. :party:
 
Last edited:
Upvote 0
Hi, Eärendil brought me here. The neg^multiplayer_is_server thing is very misleading. This is processed by the Python-based compiler when generating the weird lines that end up in the .txt files and it only works at all because the bits of both numbers don't touch.

Python:
>>> hex(0x80000000 | 417)
'0x800001a1'
>>> hex(0x80000000 ^ 417)
'0x800001a1'

The game sees the same M&B operation code / opcode. So what? Why? ¯\_(ツ)_/¯

An exclusive OR (XOR) works like a plain OR whenever both bits are not one, see the truth table here and compare XOR with OR. So using this for flags is a bit like falling sideways while going down a rail but sticking the landing; ^ technically produces the same result, but when you want to combine flags in a bitfield with the rest of a number you really want to use the | operation to merge all the 1 bits from top and bottom into the same number, you don't really want that your bits return zero whenever the top and bottom bits are one, even if in this case does not happen, so you don't get this unintentional behavior with these particular module system numbers.

And I don't want to sound mean, but don't think the precision argument has any merit in this case because you are not shifting/rotating bits left or right, they stay in place, and they are of the same type/size. Sounds a bit like programming snake oil.

Python:
10000000000000000000000000000000 # 0x80000000 => 2147483648 in decimal => neg
                       110100001 #      0x1a1 =>        417 in decimal => multiplayer_is_server
-------------------------------- # ----------
10000000000000000000000110100001 # 0x800001a1 = (0x80000000 | 0x1a1) which is the same as doing (0x80000000 | 417)


In the table above, if you do an OR operation, as long as there is some 1 in the input (e.g. 1|1, 0|1, 1|0) it will output one, for XOR if both are one (1^1) will be zero, this is generally used to negate or toggle boolean variables easily. Turning True into False and vice versa.

TL;DR: Don't get me wrong, XOR is very useful for many other low-level and fun operations, but it shouldn't be used for concatenating flags. I can use other unrelated operations that also give the same result and look cool, but that doesn't make it right.

Python:
>>> hex(0x80000000 + 417)
'0x800001a1'
>>> hex((0x80000000 - ~417) - 1)
'0x800001a1'
>>> hex(~(~0x80000000 & ~417)) # swy: same as OR, but obfuscated with NAND
'0x800001a1'

Hope that helps at least a bit. :party:
I see now. Didn't think that way before.

Edit: I just updated and collision grew but not texture and use symbol's area when I approach from green side. Here is whole code

Code:
("game_receive_network_message",
    [
        (store_script_param, ":player_id", 1),
        (store_script_param, ":event_type", 2),
        (try_begin),
            (neg|multiplayer_is_server),
            (try_begin),
                ... # I hid this part so its more clearer
            (else_try),
                (eq, ":event_type", client_event_player_set_scene_prop_scale),
                (set_fixed_point_multiplier, 1000),
                (try_for_prop_instances, ":prop_instance",  "spr_barrier_20m"),
                    (scene_prop_get_slot, ":prop_slot_x", ":prop_instance", scene_prop_slot_scale_x),
                    (scene_prop_get_slot, ":prop_slot_y", ":prop_instance", scene_prop_slot_scale_y),
                    (scene_prop_get_slot, ":prop_slot_z", ":prop_instance", scene_prop_slot_scale_z),
                    (gt, ":prop_slot_x", 0),
                    (gt, ":prop_slot_y", 0),
                    (gt, ":prop_slot_z", 0),
                    (prop_instance_set_scale, ":prop_instance", ":prop_slot_x", ":prop_slot_y", ":prop_slot_z"),
                    (rebuild_shadow_map),
                (try_end),
            (try_end),
        (else_try),

Code:
("spawn_boundaries",
    [
        (set_fixed_point_multiplier, 1000),
        (init_position, pos0),
        (get_scene_boundaries, pos1, pos2),
        (position_get_x, ":pos1x",pos1),
        (position_get_x, ":x", pos2),
        (val_add, ":x", ":pos1x"),
        (val_div, ":x", 2),
        (position_get_x, ":pos1y",pos1),
        (position_get_y, ":y", pos2),
        (val_add, ":y", ":pos1y"),
        (val_div, ":y", 2),
        (try_for_range, ":count", 0, 4),
            (try_begin),
                (eq, ":count", 0), #north
                (store_mul, ":newy", ":y", 2),
                (store_div, ":minusy", ":newy", 8),
                (val_sub, ":newy", ":minusy"),
                (position_set_x, pos0, ":x"),
                (position_set_y, pos0, ":newy"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_scale_x, reg1, pos1),
                (val_mul, reg1, 100),
                (position_get_scale_y, reg2, pos1),
                (position_get_scale_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_x, pos1, reg1),
                (position_set_scale_z, pos1, reg3),
                (position_get_scale_x, reg4, pos1),
                (position_get_scale_y, reg5, pos1),
                (position_get_scale_z, reg6, pos1),
                (prop_instance_set_scale, reg0, reg4, reg5, reg6),
                (prop_instance_set_position, reg0, pos0),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_x, reg4),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_y, reg5),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_z, reg6),
            (else_try),
                (eq, ":count", 1), #east
                (store_mul, ":newx", ":x", 2),
                (store_div, ":minusx", ":newx", 8),
                (val_sub, ":newx", ":minusx"),
                (position_set_x, pos0, ":newx"),
                (position_set_y, pos0, ":y"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_scale_x, reg1, pos1),
                (position_get_scale_y, reg2, pos1),
                (val_mul, reg2, 100),
                (position_get_scale_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_y, pos1, reg2),
                (position_set_scale_z, pos1, reg3),
                (position_get_scale_x, reg4, pos1),
                (position_get_scale_y, reg5, pos1),
                (position_get_scale_z, reg6, pos1),
                (prop_instance_set_scale, reg0, reg4, reg5, reg6),
                (prop_instance_set_position, reg0, pos0),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_x, reg4),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_y, reg5),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_z, reg6),
            (else_try),
                (eq, ":count", 2), #south
                (store_mul, ":newy", ":y", 2),
                (val_div, ":newy", 16),
                (position_set_x, pos0, ":x"),
                (position_set_y, pos0, ":newy"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_scale_x, reg1, pos1),
                (val_mul, reg1, 100),
                (position_get_scale_y, reg2, pos1),
                (position_get_scale_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_x, pos1, reg1),
                (position_set_scale_z, pos1, reg3),
                (position_get_scale_x, reg4, pos1),
                (position_get_scale_y, reg5, pos1),
                (position_get_scale_z, reg6, pos1),
                (prop_instance_set_scale, reg0, reg4, reg5, reg6),
                (prop_instance_set_position, reg0, pos0),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_x, reg4),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_y, reg5),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_z, reg6),
            (else_try),
                (eq, ":count", 3), #west
                (store_mul, ":newx", ":x", 2),
                (val_div, ":newx", 16),
                (position_set_x, pos0, ":newx"),
                (position_set_y, pos0, ":y"),
                (position_set_z_to_ground_level, pos0),
                (set_spawn_position, pos0),
                (spawn_scene_prop, "spr_barrier_20m"),
                (prop_instance_get_scale, pos1, reg0),
                (position_get_scale_x, reg1, pos1),
                (position_get_scale_y, reg2, pos1),
                (val_mul, reg2, 100),
                (position_get_scale_z, reg3, pos1),
                (val_mul, reg3, 100),
                (position_set_scale_y, pos1, reg2),
                (position_set_scale_z, pos1, reg3),
                (position_get_scale_x, reg4, pos1),
                (position_get_scale_y, reg5, pos1),
                (position_get_scale_z, reg6, pos1),
                (prop_instance_set_scale, reg0, reg4, reg5, reg6),
                (prop_instance_set_position, reg0, pos0),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_x, reg4),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_y, reg5),
                (scene_prop_set_slot, reg0, scene_prop_slot_scale_z, reg6),
            (try_end),
        (try_end),
    ]),

Code:
(ti_after_mission_start, 0, 0, [],
[
    (try_begin),
        (multiplayer_is_server),
        (call_script, "script_spawn_skyboxes"),
        (call_script, "script_spawn_boundaries"),
    (try_end),
    (try_begin),
        ... # I hid this part so its more clearer
    (try_end),
])

Code:
(ti_server_player_joined, 0, 0, [],
[
    (store_trigger_param, ":player_id", 1),

    (player_set_team_no, ":player_id", multi_team_default),

    (multiplayer_send_message_to_player, ":player_id", client_event_player_set_scene_prop_scale),
    ... # I hid this part so its more clearer
])

 
Last edited:
Upvote 0
Back
Top Bottom