target: bpy.props.PointerProperty( \
                type=bpy.types.Object, name="destination", \
                poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate']))
-
+   
+   nonlocal_world: bpy.props.StringProperty()
    key: bpy.props.StringProperty()
    tipo: bpy.props.EnumProperty(items=(('default', 'Default', ""),
                                        ('nonlocal', 'Non-Local', "")))
    flip: bpy.props.BoolProperty( name="Flip exit", default=False )
    custom: bpy.props.BoolProperty( name="Mesh is surface", default=False )
    locked: bpy.props.BoolProperty( name="Start Locked", default=False )
+   no_linkback: bpy.props.BoolProperty( name="No Linkback", default=False )
 
    @staticmethod
    def sr_inspector( layout, data ):
       box = layout.box()
       box.prop( data[0], 'tipo', text="subtype" )
 
-      if   data[0].tipo == 'default':  box.prop( data[0], 'target' )
-      elif data[0].tipo == 'nonlocal': box.prop( data[0], 'key' )
+      if   data[0].tipo == 'default':
+      #{
+         box.prop( data[0], 'target' )
+      #}
+      elif data[0].tipo == 'nonlocal': 
+      #{
+         box.prop( data[0], 'nonlocal_world' )
+         box.prop( data[0], 'key' )
+      #}
 
       flags = box.box()
       flags.prop( data[0], 'flip' )
       flags.prop( data[0], 'custom' )
       flags.prop( data[0], 'locked' )
+
+      if data[0].tipo == 'nonlocal': 
+         flags.prop( data[0], 'no_linkback' )
    #}
 #}
 
 
                   flags |= 0x0001
                #}
             #}
-            elif obj_data.tipo == 'nonlocal':#{
-               gate.target = 0
+            elif obj_data.tipo == 'nonlocal':
+            #{
+               gate.target = _af_pack_string(obj_data.nonlocal_world)
                gate.key = _af_pack_string(obj_data.key)
                flags |= 0x0002
             #}
                      mdl_compile_mesh_internal( obj )
             #}
             if obj_data.locked: flags |= 0x0010
+            if obj_data.no_linkback: flags |= 0x0040
             gate.flags = flags
             
             gate.dimensions[0] = mesh_data.dimensions[0]
 
 #endif
 
 /* v102+ */
-enum ent_gate_flag{
+enum ent_gate_flag
+{
    k_ent_gate_linked      = 0x1, /* this is a working portal */
    k_ent_gate_nonlocal    = 0x2, /* use the key string to link this portal.
                                        NOTE: if set, it adds the flip flag. */
    k_ent_gate_locked      = 0x10,/* has to be unlocked to be useful */
 
    k_ent_gate_clean_pass  = 0x20,/* player didn't rewind while getting here */
+   k_ent_gate_no_linkback = 0x40,/* NONLOCAL Recievers are not allowed to linkback through this gate */
 };
 
 struct ent_gate{
 
    /* runtime */
    m4x3f to_world, transport;
-
-   union{
+   union
+   {
       u32 timing_version;
+      u32 addon_reg;
 
       struct{
          u8 ref_count;
 
    SDL_AtomicUnlock( &air_audio_data.sl );
 }
 
+void player__transport( m4x3f transport )
+{
+   struct player_cam_controller *cc = &localplayer.cam_control;
+   m4x3_mulv( transport, cc->tpv_lpf, cc->tpv_lpf );
+   m3x3_mulv( transport, cc->cam_velocity_smooth, cc->cam_velocity_smooth );
+   m4x3_mulv( transport, localplayer.cam.pos, localplayer.cam.pos );
+   
+   v3f v0;
+   v3_angles_vector( localplayer.angles, v0 );
+   m3x3_mulv( transport, v0, v0 );
+   v3_angles( v0, localplayer.angles );
+
+   if( player_subsystems[ localplayer.subsystem ]->transport )
+      player_subsystems[ localplayer.subsystem ]->transport( transport );
+   else
+      vg_warn( "Player passed gate, but subsystem(%d) doesn't have a transport function.\n", localplayer.subsystem );
+}
+
 /*
  * Applies gate transport to a player_interface
  */
 void player__pass_gate( u32 id )
 {
-   world_instance *world = &_world.main;
-   skaterift_record_frame( &player_replay.local, 1 );
+   audio_lock();
+   audio_oneshot( &audio_gate_pass, 1.0f, 0.0f );
+   audio_unlock();
 
-   /* update boundary hash (network animation) */
-   u16 index = mdl_entity_id_id(id) & ~NETMSG_BOUNDARY_MASK;
-   localplayer.boundary_hash ^= NETMSG_GATE_BOUNDARY_BIT;
-   localplayer.boundary_hash &= ~NETMSG_BOUNDARY_MASK;
-   localplayer.boundary_hash |= index;
-   
+   world_instance *world = &_world.main;
    ent_gate *gate = af_arritm( &world->ent_gate, mdl_entity_id_id(id) );
-   world_routes_fracture( world, gate, localplayer.rb.co, localplayer.rb.v );
 
-   localplayer.gate_waiting = gate;
-   localplayer.deferred_frame_record = 1;
+   if( gate->flags & k_ent_gate_nonlocal )
+   {
+      _world.travelled_through_nonlocal_gate = 1;
+      _world.copy_of_nonlocal_sender = *gate;
+      _world.copy_of_nonlocal_sender.target = 0;
+      _world.copy_of_nonlocal_sender.key = 0;
+      _world.copy_of_nonlocal_sender.submesh_start = 0;
+      _world.copy_of_nonlocal_sender.submesh_count = 0;
+
+      vg_strncpy( af_str( &world->meta.af, gate->key ), 
+                  _world.nonlocal_destination_key, 32, 
+                  k_strncpy_overflow_fatal );
+
+      addon_reg *reg = get_addon_from_index( k_addon_type_world, gate->addon_reg, 0 );
+      skaterift_switch_world_start( reg );
+      return;
+   }
+   else
+   {
+      player__transport( gate->transport );
 
-   struct player_cam_controller *cc = &localplayer.cam_control;
-   m4x3_mulv( gate->transport, cc->tpv_lpf, cc->tpv_lpf );
-   m3x3_mulv( gate->transport, cc->cam_velocity_smooth, 
-                               cc->cam_velocity_smooth );
+      skaterift_record_frame( &player_replay.local, 1 );
 
-   m4x3_mulv( gate->transport, localplayer.cam.pos, localplayer.cam.pos );
+      /* update boundary hash (network animation) */
+      u16 index = mdl_entity_id_id(id) & ~NETMSG_BOUNDARY_MASK;
+      localplayer.boundary_hash ^= NETMSG_GATE_BOUNDARY_BIT;
+      localplayer.boundary_hash &= ~NETMSG_BOUNDARY_MASK;
+      localplayer.boundary_hash |= index;
 
-   if( gate->flags & k_ent_gate_nonlocal )
-      vg_error( "Nonlocal gates are deprecated!\n" );
-   else 
-      world_routes_activate_entry_gate( world, gate );
-   
-   v3f v0;
-   v3_angles_vector( localplayer.angles, v0 );
-   m3x3_mulv( gate->transport, v0, v0 );
-   v3_angles( v0, localplayer.angles );
+      localplayer.gate_waiting = gate;
+      localplayer.deferred_frame_record = 1;
 
-   audio_lock();
-   audio_oneshot( &audio_gate_pass, 1.0f, 0.0f );
-   audio_unlock();
+      world_routes_fracture( world, gate, localplayer.rb.co, localplayer.rb.v );
+      world_routes_activate_entry_gate( world, gate );
+   }
 }
 
 void player_apply_transport_to_cam( m4x3f transport )
 
 
    void(*sfx_comp)(void *animator);
    void(*sfx_kill)(void);
+   void(*transport)( m4x3f transport );
 
    void *animator_data;
    u32 animator_size;
 void player__post_update(void);
 
 void player__pass_gate( u32 id );
+void player__transport( m4x3f transport );
 void player__im_gui( ui_context *ctx );
 void player__setpos( v3f pos );
 void player__spawn( ent_spawn *rp );
 
    .bind = player__skate_bind,
    .pre_update = player__skate_pre_update,
    .update = player__skate_update,
+   .transport = player__skate_transport,
    .post_update = player__skate_post_update,
    .im_gui = player__skate_im_gui,
    .animate = player__skate_animate,
    return new_activity;
 }
 
-void player__skate_update(void){
+void player__skate_update(void)
+{
    struct player_skate_state *state = &player_skate.state;
    world_instance *world = &_world.main;
 
 
    u32 id = world_intersect_gates( world, localplayer.rb.co, state->prev_pos );
 
-   if( id ){
+   if( id )
+   {
       ent_gate *gate = af_arritm( &world->ent_gate, mdl_entity_id_id(id) );
-
-      m4x3_mulv( gate->transport, localplayer.rb.co, localplayer.rb.co );
-      m3x3_mulv( gate->transport, localplayer.rb.v,  localplayer.rb.v );
-      m4x3_mulv( gate->transport, state->cog,   state->cog );
-      m3x3_mulv( gate->transport, state->cog_v, state->cog_v );
-      m3x3_mulv( gate->transport, state->throw_v, state->throw_v );
-      m3x3_mulv( gate->transport, state->head_position,
-                                  state->head_position );
-      m3x3_mulv( gate->transport, state->up_dir, state->up_dir );
-
-      v4f transport_rotation;
-      m3x3_q( gate->transport, transport_rotation );
-      q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q );
-      q_mul( transport_rotation, state->smoothed_rotation,
-                                 state->smoothed_rotation );
-      q_normalize( localplayer.rb.q );
-      q_normalize( state->smoothed_rotation );
-      rb_update_matrices( &localplayer.rb );
       player__pass_gate( id );
    }
 
    }
 }
 
+void player__skate_transport( m4x3f transport )
+{
+   struct player_skate_state *state = &player_skate.state;
+   m4x3_mulv( transport, localplayer.rb.co, localplayer.rb.co );
+   m3x3_mulv( transport, localplayer.rb.v,  localplayer.rb.v );
+   m4x3_mulv( transport, state->cog,   state->cog );
+   m3x3_mulv( transport, state->cog_v, state->cog_v );
+   m3x3_mulv( transport, state->throw_v, state->throw_v );
+   m3x3_mulv( transport, state->head_position, state->head_position );
+   m3x3_mulv( transport, state->up_dir, state->up_dir );
+
+   v4f transport_rotation;
+   m3x3_q( transport, transport_rotation );
+   q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q );
+   q_mul( transport_rotation, state->smoothed_rotation, state->smoothed_rotation );
+   q_normalize( localplayer.rb.q );
+   q_normalize( state->smoothed_rotation );
+   rb_update_matrices( &localplayer.rb );
+}
+
 void player__skate_im_gui( ui_context *ctx )
 {
    struct player_skate_state *state = &player_skate.state;
 
 void player__skate_bind         (void);
 void player__skate_pre_update   (void);
 void player__skate_update       (void);
+void player__skate_transport( m4x3f transport );
 void player__skate_post_update  (void);
 void player__skate_im_gui       ( ui_context *ctx );
 void player__skate_animate      (void);
 
    .bind = player__walk_bind,
    .pre_update = player__walk_pre_update,
    .update = player__walk_update,
+   .transport = player__walk_transport,
    .post_update = player__walk_post_update,
    .im_gui = player__walk_im_gui,
    .animate = player__walk_animate,
    }
 
    u32 id = world_intersect_gates(world, localplayer.rb.co, w->state.prev_pos);
-   if( id ){
+   if( id )
+   {
       ent_gate *gate = af_arritm( &world->ent_gate, mdl_entity_id_id(id) );
-      m4x3_mulv( gate->transport, localplayer.rb.co, localplayer.rb.co );
-      m3x3_mulv( gate->transport, localplayer.rb.v,  localplayer.rb.v );
-
-      v4f transport_rotation;
-      m3x3_q( gate->transport, transport_rotation );
-      q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q );
-      q_normalize( localplayer.rb.q );
-      rb_update_matrices( &localplayer.rb );
       player__pass_gate( id );
    }
    rb_update_matrices( &localplayer.rb );
                             k_runspeed );
 }
 
+void player__walk_transport( m4x3f transport )
+{
+   m4x3_mulv( transport, localplayer.rb.co, localplayer.rb.co );
+   m3x3_mulv( transport, localplayer.rb.v,  localplayer.rb.v );
+
+   v4f transport_rotation;
+   m3x3_q( transport, transport_rotation );
+   q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q );
+   q_normalize( localplayer.rb.q );
+   rb_update_matrices( &localplayer.rb );
+}
+
+
 void player__walk_post_update(void){
    struct player_walk *w = &player_walk;
 
 
 
 void player__walk_pre_update  (void);
 void player__walk_update      (void);
+void player__walk_transport   ( m4x3f transport );
 void player__walk_post_update (void);
 void player__walk_animate     (void);
 void player__walk_pose        (void *animator, player_pose *pose);
 
    world_instance main;
    addon_reg *default_hub_addon, *switch_to_addon;
 
+   addon_reg *previous_world_addon;
+   char nonlocal_destination_key[32];
+   bool travelled_through_nonlocal_gate;
+   ent_gate copy_of_nonlocal_sender;
+
    enum world_loader_state
    {
       k_world_loader_none,
 
    VG_ASSERT( vg_thread_purpose() == k_thread_purpose_main );
 
    world_instance *world = payload;
+   bool found_nonlocal_reciever = 0;
+
    for( u32 j=0; j<af_arrcount(&world->ent_gate); j ++ )
    {
       ent_gate *gate = af_arritm( &world->ent_gate, j );
       gate_transform_update( gate );
+
+      if( gate->flags & k_ent_gate_nonlocal )
+      {
+         if( gate->target )
+         {
+            const char *dest_world = af_str( &world->meta.af, gate->target );
+            
+            addon_alias q;
+            addon_uid_to_alias( dest_world, &q );
+
+            gate->addon_reg = addon_match( &q );
+            if( gate->addon_reg != 0xffffffff )
+            {
+               gate->flags |= k_ent_gate_linked;
+               vg_info( "Linked non-local gate to addon #%u\n", gate->addon_reg );
+            }
+            else
+            {
+               vg_error( "Reference in non-local gate to other world '%s' was not found.\n", dest_world );
+            }
+         }
+         else
+         {
+            if( _world.travelled_through_nonlocal_gate )
+            {
+               const char *key = af_str( &world->meta.af, gate->key );
+               if( vg_str_eq( key, _world.nonlocal_destination_key ) )
+               {
+                  if( found_nonlocal_reciever )
+                     vg_warn( "There are multiple nonlocal gates that share the same key in this world (%s)\n", key );
+                  else
+                  {
+                     found_nonlocal_reciever = 1;
+
+                     ent_gate *gate2 = &_world.copy_of_nonlocal_sender;
+
+                     v3_copy( gate->co[0], gate2->co[1] );
+                     v3_copy( gate2->co[0], gate->co[1] );
+                     v4_copy( gate->q[0], gate2->q[1] );
+                     v4_copy( gate2->q[0], gate->q[1] );
+
+                     if( world->meta.version < 102 )
+                     {
+                        /* LEGACY BEHAVIOUR: v101
+                         *   this would flip both the client worlds portal's entrance and
+                         *   exit. effectively the clients portal would be the opposite
+                         *   to the hub worlds one. new behaviour is to just flip the
+                         *   destinations so the rules are consistent in each world.
+                         */
+                        v4f qflip;
+                        q_axis_angle( qflip, (v3f){0.0f,1.0f,0.0f}, VG_PIf );
+                        q_mul( gate->q[0], qflip, gate->q[0] );
+                        q_mul( gate->q[1], qflip, gate->q[1] );
+                     }
+
+                     gate_transform_update( gate );
+                     gate_transform_update( gate2 );
+                     
+                     if( !(_world.copy_of_nonlocal_sender.flags & k_ent_gate_no_linkback) )
+                     {
+                        gate->addon_reg = get_index_from_addon( k_addon_type_world, _world.previous_world_addon );
+
+                        if( gate->addon_reg != 0xffffffff )
+                        {
+                           gate->flags |= k_ent_gate_linked;
+                           vg_info( "Linked non-local gate to addon #%u\n", gate->addon_reg );
+                        }
+                        else
+                        {
+                           vg_error( "Error while linking to previous world.\n" );
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   if( _world.travelled_through_nonlocal_gate && !found_nonlocal_reciever )
+   {
+      vg_error( "Player travelled through non-local gate, but no reciever gate was found. Player will default to start spawn or world save file.\n" );
+      _world.travelled_through_nonlocal_gate = 0;
    }
 }
 
 
 
    /* start entities in the world */
    world_entity_start( data->instance, &sav );
-   world_default_spawn_pos( data->instance, localplayer.rb.co );
 
    /* start player in the world */
-   vg_msg_init( &sav, data->save.buf, data->save.len );
-   vg_msg player_frame = sav;
-   if( vg_msg_seekframe( &player_frame, "player" ) )
+   if( _world.travelled_through_nonlocal_gate )
    {
-      vg_msg_getkvvecf( &player_frame, "position", k_vg_msg_v3f, 
-                        localplayer.rb.co, NULL );
+      player__transport( _world.copy_of_nonlocal_sender.transport );
+      _world.travelled_through_nonlocal_gate = 0;
+      vg_success( "Successfuly consumed linked non-local gates composite transport matrix.\n" );
+   }
+   else
+   {
+      bool restored_player_position = 0;
+
+      vg_msg_init( &sav, data->save.buf, data->save.len );
+      vg_msg player_frame = sav;
+      if( vg_msg_seekframe( &player_frame, "player" ) )
+      {
+         if( vg_msg_getkvvecf( &player_frame, "position", k_vg_msg_v3f, 
+                               localplayer.rb.co, NULL ) )
+            restored_player_position = 1;
+      }
+
+      if( !restored_player_position )
+         world_default_spawn_pos( data->instance, localplayer.rb.co );
+
+      player__reset();
    }
-   player__reset();
 }
 
 void load_player_from_world_savedata_thread( void *_args )
          return;
       }
    }
+   
+   if( reg != _world.main.addon )
+      _world.previous_world_addon = _world.main.addon;
 
    g_client.unreadyness ++;
    _world.loader_state = k_world_loader_saving_current;
 
 
       if( gate->flags & k_ent_gate_nonlocal )
       {
+         world->rendering_gate = NULL; /* this is for jump prediction only. */
+
+         if( gate->flags & k_ent_gate_linked )
+         {
+
+         }
+         else
+         {
+            render_gate_unlinked( world, gate, cam );
+         }
       }
       else
          render_gate( world, world, gate, cam );
 
       route->active_checkpoint = 0xffff;
    }
 
-   for( u32 i=0; i<af_arrcount( &world->ent_gate ); i++ ){
+   for( u32 i=0; i<af_arrcount( &world->ent_gate ); i++ )
+   {
       ent_gate *rg = af_arritm( &world->ent_gate, i );
-      rg->timing_version = 0;
-      rg->timing_time = 0.0;
+
+      if( !(rg->flags & k_ent_gate_nonlocal) )
+      {
+         rg->timing_version = 0;
+         rg->timing_time = 0.0;
+      }
    }
 
    _world.current_run_version += 4;
                                                   &world->mesh_route_lines,
                                                   200000, 300000 );
 
-   for( u32 i=0; i<af_arrcount(&world->ent_gate); i++ ){
+   for( u32 i=0; i<af_arrcount(&world->ent_gate); i++ )
+   {
       ent_gate *gate = af_arritm( &world->ent_gate, i );
-      gate->ref_count = 0;
-      gate->route_count = 0;
+
+      if( !(gate->flags & k_ent_gate_nonlocal) )
+      {
+         gate->ref_count = 0;
+         gate->route_count = 0;
+      }
    }
 
-   for( u32 i=0; i<af_arrcount(&world->ent_route_node); i++ ){
+   for( u32 i=0; i<af_arrcount(&world->ent_route_node); i++ )
+   {
       ent_route_node *rn = af_arritm( &world->ent_route_node, i );
       rn->ref_count = 0;
       rn->ref_total = 0;