UDN
Search public documentation:

ReplicationProxy
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > Networking & Replication > Proxy pattern

Proxy pattern


Overview


The proxy pattern is used when you have actors or objects which don't exist on the client, but the clients need to visualize or hear them. Because those actors don't exist on the client, there is no way the server can replicate directly to the client. Objects don't support replication at all. You use the proxy pattern to simulate what an actor or object is doing on the server, on the client through the proxy without having to replicate the proxy itself.

Weapon attachments


This pattern is used in Unreal Tournament for weapon attachments. Weapons in Unreal Engine 3 are only relevant between the server and client that owns the weapon. However, clients also need to some information about the weapon the clients are wielding. This is done by creating the weapon attachment on the server and then replicating it to all of the clients. Both the server and the client then spawn the weapon attachment actor. As the weapon is updated on the server, the weapon tells the pawn owner of the changes. These changes are then replicated to the client, which then updates the client weapon attachment.

UTPawn.uc
var repnotify class<UTWeaponAttachment> CurrentWeaponAttachmentClass;
var UTWeaponAttachment CurrentWeaponAttachment;

replication
{
  if (bNetDirty)
    CurrentWeaponAttachmentClass;
}

simulated event ReplicatedEvent(name VarName)
{
  if (VarName == 'CurrentWeaponAttachmentClass')
  {
    WeaponAttachmentChanged();
  }
}

simulated function WeaponAttachmentChanged()
{
  if ((CurrentWeaponAttachment == None || CurrentWeaponAttachment.Class != CurrentWeaponAttachmentClass) && Mesh.SkeletalMesh != None)
  {
    if (CurrentWeaponAttachment!=None)
    {
      CurrentWeaponAttachment.DetachFrom(Mesh);
      CurrentWeaponAttachment.Destroy();
    }

    if (CurrentWeaponAttachmentClass!=None)
    {
      CurrentWeaponAttachment = Spawn(CurrentWeaponAttachmentClass,self);
      CurrentWeaponAttachment.Instigator = self;
    }
    else
      CurrentWeaponAttachment = none;

    if (CurrentWeaponAttachment != None)
    {
      CurrentWeaponAttachment.AttachTo(self);
      CurrentWeaponAttachment.SetSkin(ReplicatedBodyMaterial);
      CurrentWeaponAttachment.ChangeVisibility(bWeaponAttachmentVisible);
    }
  }
}

simulated function WeaponFired(Weapon InWeapon, bool bViaReplication, optional vector HitLocation)
{
  if (CurrentWeaponAttachment != None)
  {
    if (!IsFirstPerson())
    {
      CurrentWeaponAttachment.ThirdPersonFireEffects(HitLocation);
    }
    else
    {
      CurrentWeaponAttachment.FirstPersonFireEffects(Weapon, HitLocation);

      if (class'Engine'.static.IsSplitScreen() && CurrentWeaponAttachment.EffectIsRelevant(CurrentWeaponAttachment.Location, false, CurrentWeaponAttachment.MaxFireEffectDistance))
      {
        CurrentWeaponAttachment.CauseMuzzleFlash();
      }
    }

    if (HitLocation != Vect(0,0,0) && (WorldInfo.NetMode == NM_ListenServer || WorldInfo.NetMode == NM_Standalone || bViaReplication))
    {
      CurrentWeaponAttachment.PlayImpactEffects(HitLocation);
    }
  }
}

UTWeaponAttachment.uc
simulated function CauseMuzzleFlash()
{
  local ParticleSystem MuzzleTemplate;

  if ((!WorldInfo.bDropDetail && !class'Engine'.static.IsSplitScreen()) || WorldInfo.IsConsoleBuild(CONSOLE_Mobile))
  {
    if (MuzzleFlashLight == None)
    {
      if (MuzzleFlashLightClass != None)
      {
        MuzzleFlashLight = new(Outer) MuzzleFlashLightClass;
   
        if (Mesh != None && Mesh.GetSocketByName(MuzzleFlashSocket) != None)
        {
          Mesh.AttachComponentToSocket(MuzzleFlashLight, MuzzleFlashSocket);
        }
        else if (OwnerMesh != None)
        {
          OwnerMesh.AttachComponentToSocket(MuzzleFlashLight, AttachmentSocket);
        }
      }
    }
    else
    {
      MuzzleFlashLight.ResetLight();
    }
  }

  if (MuzzleFlashPSC != none)
  {
    if (!bMuzzleFlashPSCLoops || !MuzzleFlashPSC.bIsActive)
    {
      if (Instigator != None && Instigator.FiringMode == 1 && MuzzleFlashAltPSCTemplate != None)
      {
        MuzzleTemplate = MuzzleFlashAltPSCTemplate;
      }
      else
      {
        MuzzleTemplate = MuzzleFlashPSCTemplate;
      }
   
      if (MuzzleTemplate != MuzzleFlashPSC.Template)
      {
        MuzzleFlashPSC.SetTemplate(MuzzleTemplate);
      }
      
      SetMuzzleFlashParams(MuzzleFlashPSC);
      MuzzleFlashPSC.ActivateSystem();
    }
  }

  SetTimer(MuzzleFlashDuration, false, 'MuzzleFlashTimer');
}

simulated function ThirdPersonFireEffects(vector HitLocation)
{
  local UTPawn P;

  if (EffectIsRelevant(Location,false,MaxFireEffectDistance))
  {
    CauseMuzzleFlash();
  }

  P = UTPawn(Instigator);
  if (P != None && P.GunRecoilNode != None)
  {
    P.GunRecoilNode.bPlayRecoil = true;
  }

  if (Instigator.FiringMode == 1 && AltFireAnim != 'None')
  {
    Mesh.PlayAnim(AltFireAnim,,, false);
  }
  else if (FireAnim != 'None')
  {
    Mesh.PlayAnim(FireAnim,,, false);
  }
}

Notes

Both UTPawn and UTWeaponAttachment are only showing specific functions, check the source files to view the entire logic.

Conclusion


This pattern is used when the client needs to visualize or hear effects based on actors or objects that don't exist on the client. Other than weapon attachments, other useful instances where this pattern can be useful are:
  • Armor/Clothing attachments that have game play effects
  • Turret attachments on vehicles that have game play effects