UDN
Search public documentation:

ReplicationPatternRepNotifyCH
English Translation
日本語訳
한국어

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 主页 > 网络&复制 > RepNotify(复制通知) 模式

RepNotify(复制通知) 模式


概述


RepNotify是一个变量属性标志,当您想检测变量是否由于复制已经发生改变时可以使用该标志。这个模式仅用于客户端,因为客户端必须使用客户端到服务器远程调用来将数据发送到服务器。使用这个模式的常见时机是当您想根据复制的变量更新客户端特效时。

安全注意事项


一般情况下,您不想让客户端在没有某种形式的数据验证或检测情况下就简单地修改相关的数据。但为了简化这个示例,这里已经 忽略 了安全问题。

ExampleReplicationInfo 类


这个复制信息简单地包含了一些可以在服务器和客户端上同时修改的信息。当变量在服务器上更新时,将自动地将它们重新复制到客户端上。一旦变量复制完成,就调用ReplicatedEvent 。

RN_ExampleReplicationInfo.uc
class RN_ExampleReplicationInfo extends ReplicationInfo;

var RepNotify int IntExample;
var RepNotify byte ByteExample;
var RepNotify float FloatExample;
var RepNotify String StringExample;
var RepNotify Vector VectorExample;
var RepNotify Rotator RotatorExample;
var RepNotify Color ColorExample;

replication
{
  // Replicate when dirty
  if (bNetDirty)
    IntExample, ByteExample, FloatExample, StringExample, VectorExample, RotatorExample, ColorExample;
}

/**
 * PostBeginPlay is executed after the RN_ExampleReplicationInfo is created
 *
 * Network: All
 */
simulated function PostBeginPlay()
{
  Super.PostBeginPlay();

  if (Role == Role_Authority)
  {
    `Log(Self$":: PostBeginPlay():: Executed on the server.");
  }
  else
  {
    `Log(Self$":: PostBeginPlay():: Executed on the client.");
  }
}

/**
 * ReplicatedEvent is called when a variable with the RepNotify property flag is updated
 *
 * Network: All
 */
simulated event ReplicatedEvent(name VarName)
{
  local String Text;

  if (Role == Role_Authority)
  {
    Text = "Server";
  }
  else
  {
    Text = "Client";
  }

  switch (VarName)
  {
  case 'IntExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: IntExample is now "$IntExample$".");
    break;

  case 'ByteExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: ByteExample is now "$ByteExample$".");
    break;

  case 'FloatExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: FloatExample is now "$FloatExample$".");
    break;

  case 'StringExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: StringExample is now '"$StringExample$"'.");
    break;

  case 'VectorExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: VectorExample is now (X="$VectorExample.X$", Y="$VectorExample.Y$", Z="$VectorExample.Z$").");
    break;

  case 'RotatorExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: RotatorExample is now (Pitch="$RotatorExample.Pitch$", Yaw="$RotatorExample.Yaw$", Roll="$RotatorExample.Roll$").");
    break;

  case 'ColorExample':
    `Log(Self$":: ReplicatedEvent():: "$Text$":: ColorExample is now (R="$ColorExample.R$", G="$ColorExample.G$", B="$ColorExample.B$", A="$ColorExample.A$").");
    break;
  }
}

defaultproperties
{
}

Player Controllers(玩家控制器)类


玩家控制器类允许客户端修改ExampleReplicationInfo中所包含的变量。为了完成这个处理,玩家控制器发送一个远程调用到它的服务器版本,然后该版本在服务器端修改ExampleReplicationInfo。当服务器端的变量改变后,然后将它们复制回客户端。在现实世界的仿真中,您不必考虑等待信息返程的问题,如果允许您可以简单地更新ExampleReplicationInfo的客户端版本。但是,如果需要某些服务器端验证和逻辑才能完成更新,那么信息返回的等待是不可避免的。为了在简化这个模式示例已经忽略了这种真实世界中的仿真。在这个示例中,注意当服务器端的ExampleReplicationInfo版本发生改变时,同时会调用ReplicatedEvent。这将使得服务器执行任何变量发生改变时所执行的逻辑。

RN_PlayerController.uc
class RN_PlayerController extends PlayerController;

/**
 * Returns an instance of Example Replication Info
 *
 * Network: Dedicated/Listen Server
 */
function RN_ExampleReplicationInfo GetExampleReplicationInfoInstance()
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ForEach DynamicActors(class'RN_ExampleReplicationInfo', ExampleReplicationInfo)
  {
    return ExampleReplicationInfo;
  }
}

/**
 * RPC to update the int
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateInt(int I)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateInt:: Updating int with "$I$".");
    ExampleReplicationInfo.IntExample = I;
    ExampleReplicationInfo.ReplicatedEvent('IntExample');
  }
}

/**
 * RPC to update the byte
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateByte(byte B)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateByte:: Updating byte with "$B$".");
    ExampleReplicationInfo.ByteExample = B;
    ExampleReplicationInfo.ReplicatedEvent('ByteExample');
  }
}

/**
 * RPC to update the float
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateFloat(float F)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateFloat:: Updating float with "$F$".");
    ExampleReplicationInfo.FloatExample = F;
    ExampleReplicationInfo.ReplicatedEvent('FloatExample');
  }
}

/**
 * RPC to update the string
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateString(string S)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateString:: Updating string with "$S$".");
    ExampleReplicationInfo.StringExample = S;
    ExampleReplicationInfo.ReplicatedEvent('StringExample');
  }
}

/**
 * RPC to update the vector
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateVector(Vector V)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateVector:: Updating vector with (X="$V.X$", Y="$V.Y$", Z="$V.Z$").");
    ExampleReplicationInfo.VectorExample = V;
    ExampleReplicationInfo.ReplicatedEvent('VectorExample');
  }
}

/**
 * RPC to update the rotator
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateRotator(Rotator R)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateRotator:: Updating rotator with (Pitch="$R.Pitch$", Yaw="$R.Yaw$", Roll="$R.Roll$").");
    ExampleReplicationInfo.RotatorExample = R;
    ExampleReplicationInfo.ReplicatedEvent('RotatorExample');
  }
}

/**
 * RPC to update the color
 *
 * Network: Dedicated/Listen Server
 */
reliable server function ServerUpdateColor(Color C)
{
  local RN_ExampleReplicationInfo ExampleReplicationInfo;

  ExampleReplicationInfo = GetExampleReplicationInfoInstance();

  if (ExampleReplicationInfo != None)
  {
    `Log(Self$":: ServerUpdateColor:: Updating color with (R="$C.R$", G="$C.G$", B="$C.B$", A="$C.A$").");
    ExampleReplicationInfo.ColorExample = C;
    ExampleReplicationInfo.ReplicatedEvent('ColorExample');
  }
}

/**
 * Allows the player controller update the int
 *
 * Network: Local
 */
exec function UpdateInt(int I)
{
  if (Role < Role_Authority)
  {
    ServerUpdateInt(I);
  }
}

/**
 * Allows the player controller update the byte
 *
 * Network: Local
 */
exec function UpdateByte(byte B)
{
  if (Role < Role_Authority)
  {
    ServerUpdateByte(B);
  }
}

/**
 * Allows the player controller update the float
 *
 * Network: Local
 */
exec function UpdateFloat(float F)
{
  if (Role < Role_Authority)
  {
    ServerUpdateFloat(F);
  }
}

/**
 * Allows the player controller update the string
 *
 * Network: Local
 */
exec function UpdateString(string S)
{
  if (Role < Role_Authority)
  {
    ServerUpdateString(S);
  }
}

/**
 * Allows the player controller update the vector
 *
 * Network: Local
 */
exec function UpdateVector(float X, float Y, float Z)
{
  local Vector V;

  if (Role < Role_Authority)
  {
    V.X = X;
    V.Y = Y;
    V.Z = Z;
    ServerUpdateVector(V);
  }
}

/**
 * Allows the player controller update the rotator
 *
 * Network: Local
 */
exec function UpdateRotator(int Pitch, int Yaw, int Roll)
{
  local Rotator R;

  if (Role < Role_Authority)
  {
    R.Pitch = Pitch;
    R.Yaw = Yaw;
    R.Roll = Roll;
    ServerUpdateRotator(R);
  }
}

/**
 * Allows the player controller update the color
 *
 * Network: Local
 */
exec function UpdateColor(int R, int G, int B, int A)
{
  local Color C;

  if (Role < Role_Authority)
  {
    C.R = R;
    C.G = G;
    C.B = B;
    C.A = A;
    ServerUpdateColor(C);
  }
}

defaultproperties
{
}

GameInfo 类


游戏信息类用于使用随机值更新ExampleReplicationInfo 。这为了查看正在运作中的RepNotify方法。因为游戏信息仅在客户端侧,所以可以直接地修改ExampleReplicationInfo,不必执行进一步的复制逻辑。

RN_GameInfo.uc
class RN_GameInfo extends GameInfo;

var RN_ExampleReplicationInfo ExampleReplicationInfo;
var const String Alphabet[26];

/**
 * PostBeginPlay is executed after the GameInfo is created on the server
 *
 * Network: Dedicated/Listen Server
 */
function PostBeginPlay()
{
  Super.PostBeginPlay();

  // Create the example replication info
  ExampleReplicationInfo = Spawn(class'RN_ExampleReplicationInfo');

  // Start the timer so that fresh data is sent to clients
  if (ExampleReplicationInfo != None)
  {
    SetTimer(10.f, true, 'UpdateExampleReplicationInfo');
  }
}

/**
 * Updates the example replication info and sends the new data to the client
 *
 * Network: Dedicated/Listen Server
 */
function UpdateExampleReplicationInfo()
{
  local int Index, i;
  local String Text;
  local Vector V;
  local Rotator R;
  local Color C;
  local Controller Controller;

  if (ExampleReplicationInfo == None || WorldInfo == None)
  {
    return;
  }

  // Ensure that we have players connected
  ForEach WorldInfo.AllControllers(class'Controller', Controller)
  {
    Index = int(RandRange(0.f, 7.f));

    switch (Index)
    {
    case 0: // int
      ExampleReplicationInfo.IntExample = int(RandRange(-32768, 32768));
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating int with "$ExampleReplicationInfo.IntExample$".");
      break;

    case 1: // byte
      ExampleReplicationInfo.ByteExample = byte(RandRange(0, 255));
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating byte with "$ExampleReplicationInfo.ByteExample$".");
      break;

    case 2: // float
      ExampleReplicationInfo.FloatExample = RandRange(-1234.5678, 1234.5678);
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating float with "$ExampleReplicationInfo.FloatExample$".");
      break;

    case 3: // string
      for (i = 0; i < 32; ++i)
      {
        Text $= Alphabet[Rand(26)];
      }
      ExampleReplicationInfo.StringExample = Text;
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating string with "$ExampleReplicationInfo.StringExample$".");
      break;

    case 4: // vector
      V.X = RandRange(-1234.5678, 1234.5678);
      V.Y = RandRange(-1234.5678, 1234.5678);
      V.Z = RandRange(-1234.5678, 1234.5678);
      ExampleReplicationInfo.VectorExample = V;
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating vector with (X="$V.X$", Y="$V.Y$", Z="$V.Z$")");
      break;

    case 5: // rotator
      R.Pitch = Rand(65535);
      R.Yaw = Rand(65535);
      R.Roll = Rand(65535);
      ExampleReplicationInfo.RotatorExample = R;
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating rotator with (Pitch="$R.Pitch$", Yaw="$R.Yaw$", Roll="$R.Roll$")");
      break;

    case 6: // color
      C.R = Rand(255);
      C.G = Rand(255);
      C.B = Rand(255);
      C.A = Rand(255);
      ExampleReplicationInfo.ColorExample = C;
      `Log(Self$":: UpdateExampleReplicationInfo():: Updating color with (R="$C.R$", G="$C.G$", B="$C.B$", A="$C.A$")");
      break;
    }

    break;
  }
}

defaultproperties
{
  PlayerControllerClass=class'RN_PlayerController'
  Alphabet(0)="A"
  Alphabet(1)="B"
  Alphabet(2)="C"
  Alphabet(3)="D"
  Alphabet(4)="E"
  Alphabet(5)="F"
  Alphabet(6)="G"
  Alphabet(7)="H"
  Alphabet(8)="I"
  Alphabet(9)="J"
  Alphabet(10)="K"
  Alphabet(11)="L"
  Alphabet(12)="M"
  Alphabet(13)="N"
  Alphabet(14)="O"
  Alphabet(15)="P"
  Alphabet(16)="Q"
  Alphabet(17)="R"
  Alphabet(18)="S"
  Alphabet(19)="T"
  Alphabet(20)="U"
  Alphabet(21)="V"
  Alphabet(22)="W"
  Alphabet(23)="X"
  Alphabet(24)="Y"
  Alphabet(25)="Z"
}

测试


客户端上:
  • 红色 - 这显示了通过复制在客户端上产生的 ExampleReplicationInfo
  • 绿色 - 这显示了当 FloatExample 变量的值在服务器端发生改变时会复制 FloatExample 变量。
  • 蓝色 - 这显示了当客户端使得 IntExample 变量的值在服务器端发生改变时复制了该变量。
RepNotifyClientConsoleWindow.png

服务器上:

  • 红色 - 这显示了服务器上产生了 ExampleReplicationInfo
  • 绿色 - 这显示服务器修改了 FloatExample 的值。
  • 蓝色 - 这显示客户端修改了 IntExample 的值。当服务器修改这个值时调用了ReplicatedEvent,这样就不需要复制它所包含的逻辑了。
RepNotifyServerConsoleWindow.png

下载


  • 下载这个模式中所使用的源码。(RepNotifyExample.zip)