Bluelua 支持在 lua 中重写 UE4 网络事件
最近抽空把 Bluelua 计划的最后一部分功能内容做完了,就是在 lua 中重载 UE4 中的网络事件,这样就可以直接在 lua 中重写网络相关的逻辑了。还有就是将之前重载纯蓝图函数和事件几个崩溃修复了。网络相关的重写示例在 BlueluaDemo 的 NetTest 文件夹中
UE4 中网络事件分两种,一种是 C++ 中的网络事件,就是在 UFUNCTION
中带上 Server/NetMulticast/Client
关键字,另一种是在蓝图中,创建一个 Custom Event,然后在这个事件的复制属性中选择 Run On Server/Multicast/Run on owning Client
,如图
这两种是互相独立的,也就是 C++ 中的 Server/NetMulticast/Client
函数是无法在蓝图中进行重写,所以如果有这样的需求就需要在 C++ 的 Server/NetMulticast/Client
函数中去调用其它 BlueprintNativeEvent/BlueprintImplementable
函数,将这个事件抛到蓝图中,略显麻烦。Bluelua 中就不用这么麻烦了,现在可以直接在 lua 中分别重写这两类网络事件
重写 C++ 网络事件
首先在 ANetCharacter
的 C++ 类中定义三个函数,一个可复制属性和属性的修改通知函数
UFUNCTION(Unreliable, Server, WithValidation)
void TestNativeServerFunction();
UFUNCTION(Unreliable, NetMulticast)
void TestNativeNetMulticastFunction();
UFUNCTION(Unreliable, Client)
void TestNativeClientFunction();
UPROPERTY(ReplicatedUsing=OnRep_Counter)
int32 Counter;
UFUNCTION(BlueprintNativeEvent)
void OnRep_Counter();
实现这几个函数
void ANetCharacter::TestNativeClientFunction_Implementation()
{
UE_LOG(LogTemp, Display, TEXT("%sTestNativeClientFunction get called"), *GetPrefix(this));
}
void ANetCharacter::TestNativeNetMulticastFunction_Implementation()
{
UE_LOG(LogTemp, Display, TEXT("%sTestNativeNetMulticastFunction get called"), *GetPrefix(this));
}
void ANetCharacter::TestNativeServerFunction_Implementation()
{
UE_LOG(LogTemp, Display, TEXT("%sTestNativeServerFunction get called"), *GetPrefix(this));
TestNativeNetMulticastFunction(); // will run on local and remote
TestNativeClientFunction(); // will run on remote
}
bool ANetCharacter::TestNativeServerFunction_Validate()
{
return true;
}
void ANetCharacter::OnRep_Counter_Implementation()
{
UE_LOG(LogTemp, Display, TEXT("%sNative OnRep_Counter: %d"), *GetPrefix(this), Counter);
}
// 在服务器的 Tick 中每隔 1 秒递增 Counter
void ANetCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (Role == ROLE_Authority)
{
static float UpdateTime = 0;
UpdateTime += DeltaTime;
if (UpdateTime > 1.f)
{
++Counter;
UpdateTime = 0.f;
}
}
}
在 ANetCharacter 的子类 lua 中绑定一个 P 键输入事件,按下 P 键后从客户端调用 TestNativeServerFunction/TestNativeNetMulticastFunction/TestNativeClientFunction
几个函数进行测试
function m:OnSetupPlayerInputComponent()
local BlueluaLibrary = LoadClass('BlueluaLibrary')
local EInputEvent = {
IE_Pressed = 0,
IE_Released = 1,
IE_Repeat = 2,
IE_DoubleClick = 3,
IE_Axis = 4,
IE_MAX = 5,
}
-- Press P key to start ent test
BlueluaLibrary:BindKeyAction(Super, { Key = { KeyName = 'P' } }, EInputEvent.IE_Pressed, true, false, CreateFunctionDelegate(Super, self, self.OnKeyPressed))
end
function m:OnKeyPressed()
-- test net event in c++
Super:TestNativeClientFunction() -- will run on current client
Super:TestNativeNetMulticastFunction() -- will run on current client
Super:TestNativeServerFunction() -- will run on remote server
end
在测试前需要在编辑器中勾选 Run Dedicated Server,如图
在没有重写的情况下,调用的是 C++ 中的实现,输出的 log 为
LogTemp: Display: Client 1: Native OnRep_Counter: 1
LogTemp: Display: Client 1: Native OnRep_Counter: 2
LogTemp: Display: Client 1: Native OnRep_Counter: 3
LogTemp: Display: Client 1: Native OnRep_Counter: 4
LogTemp: Display: Client 1: Native OnRep_Counter: 5
LogTemp: Display: Client 1: Native OnRep_Counter: 6
LogTemp: Display: Client 1: Native OnRep_Counter: 7
LogTemp: Display: Client 1: TestNativeClientFunction get called
LogTemp: Display: Client 1: TestNativeNetMulticastFunction get called
LogTemp: Display: Server: TestNativeServerFunction get called
LogTemp: Display: Server: TestNativeNetMulticastFunction get called
LogTemp: Display: Client 1: TestNativeClientFunction get called
LogTemp: Display: Client 1: TestNativeNetMulticastFunction get called
LogTemp: Display: Client 1: Native OnRep_Counter: 8
LogTemp: Display: Client 1: Native OnRep_Counter: 9
LogTemp: Display: Client 1: Native OnRep_Counter: 10
从 log 中可以看出,当客户端调用 Client/NetMulticast 函数时,是在本地执行的,当调用 Server 函数时,会在服务器执行。服务器上调用 NetMulticast 函数会在服务器本地和客户端上执行,调用 Client 函数会在对应的主控(Autonomous)客户端上执行
现在在 lua 中重写这些函数的实现,方法就是直接声明一个同名的函数,如下
-- override Server replicated event in c++
function m:TestNativeClientFunction()
print('TestNativeClientFunction get called')
end
-- override NetMulticast replicated event in c++
function m:TestNativeNetMulticastFunction()
print('TestNativeNetMulticastFunction get called')
end
-- override Client replicated event in c++
function m:TestNativeServerFunction()
print('TestNativeServerFunction get called')
Super:TestNativeNetMulticastFunction() -- will run on local server and remote client
Super:TestNativeClientFunction() -- will run on remote client
end
-- override property replicated event in c++
function m:OnRep_Counter()
print('OnRep_Counter:', Super.Counter)
end
按 P 键进行测试,得到得 log 输出为
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 1
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 2
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 3
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 4
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 5
LogBluelua: Display: Client 1: Lua log: TestNativeClientFunction get called
LogBluelua: Display: Client 1: Lua log: TestNativeNetMulticastFunction get called
LogBluelua: Display: Server: Lua log: TestNativeServerFunction get called
LogBluelua: Display: Server: Lua log: TestNativeNetMulticastFunction get called
LogBluelua: Display: Client 1: Lua log: TestNativeClientFunction get called
LogBluelua: Display: Client 1: Lua log: TestNativeNetMulticastFunction get called
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 6
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 7
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 8
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 9
LogBluelua: Display: Client 1: Lua log: OnRep_Counter: 10
从 log 中可以看出,所有的网络事件都正确调到 lua 中重写的实现中了,并且执行一致
重写蓝图网络事件
同样在 NetCharacter 蓝图中创建三个 Custom Event,并分别选择 Run On Server/Multicast/Run on owning Client
。创建一个 BPCounter 属性,选择 RepNotify,之后蓝图中会自动创建一个函数 OnRep_BPCounter,在这些函数中分别打印一句 log,如图
同样在 lua 的按键事件中调用这三个网络事件
function m:OnKeyPressed()
-- test net event in c++
--Super:TestNativeClientFunction() -- will run on current client
--Super:TestNativeNetMulticastFunction() -- will run on current client
--Super:TestNativeServerFunction() -- will run on remote server
-- test net event in blueprint
Super:TestBPClientFunction() -- will run on current client
Super:TestBPNetMulticastFunction() -- will run on current client
Super:TestBPServerFunction() -- will run on remote server
end
在 lua 没有重载的情况下的 log 输出为
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 1
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 1
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 2
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 2
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 3
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 3
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 4
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 4
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 5
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 5
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: TestBPClientFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: TestBPNetMulticastFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Server: TestBPServerFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Server: TestBPNetMulticastFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: TestBPClientFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: TestBPNetMulticastFunction get called
LogBlueprintUserMessages: [NetCharacter_C_0] Server: BP OnRep_BPCounter: 6
LogBlueprintUserMessages: [NetCharacter_C_0] Client 1: BP OnRep_BPCounter: 6
从 log 中可以看出,蓝图的 Run On Server/Multicast/Run on owning Client
事件和 C++ 的 Server/NetMulticast/Client
函数的执行规则是一致的,唯一的区别是可复制属性的 RepNotify 和 ReplicatedUsing,RepNotify 会在服务器本地也调用 OnRep_BPCounter 函数,而 ReplicatedUsing 不会,这一点需要注意
现在在 lua 中重写这些事件,同样只要声明一个同名的函数就行了,如下
-- override Server replicated event in blueprint
function m:TestBPClientFunction()
print('TestBPClientFunction get called')
end
-- override NetMulticast replicated event in blueprint
function m:TestBPNetMulticastFunction()
print('TestBPNetMulticastFunction get called')
end
-- override Client replicated event in blueprint
function m:TestBPServerFunction()
print('TestBPServerFunction get called')
Super:TestBPNetMulticastFunction() -- will run on local server and remote client
Super:TestBPClientFunction() -- will run on remote client
end
-- override property replicated event in blueprint
function m:OnRep_BPCounter()
print('OnRep_BPCounter:', Super.BPCounter)
end
重新按 P 键进行测试,得到得 log 输出为
LogBluelua: Display: Server: Lua log: OnRep_BPCounter: 1
LogBluelua: Display: Client 1: Lua log: OnRep_BPCounter: 1
LogBluelua: Display: Server: Lua log: OnRep_BPCounter: 2
LogBluelua: Display: Client 1: Lua log: OnRep_BPCounter: 2
LogBluelua: Display: Server: Lua log: OnRep_BPCounter: 3
LogBluelua: Display: Client 1: Lua log: OnRep_BPCounter: 3
LogBluelua: Display: Server: Lua log: OnRep_BPCounter: 4
LogBluelua: Display: Client 1: Lua log: OnRep_BPCounter: 4
LogBluelua: Display: Server: Lua log: OnRep_BPCounter: 5
LogBluelua: Display: Client 1: Lua log: OnRep_BPCounter: 5
LogBluelua: Display: Client 1: Lua log: TestBPClientFunction get called
LogBluelua: Display: Client 1: Lua log: TestBPNetMulticastFunction get called
LogBluelua: Display: Server: Lua log: TestBPServerFunction get called
LogBluelua: Display: Server: Lua log: TestBPNetMulticastFunction get called
LogBluelua: Display: Client 1: Lua log: TestBPClientFunction get called
LogBluelua: Display: Client 1: Lua log: TestBPNetMulticastFunction get called
可以看出,蓝图的网络事件也调到了 lua 中了,并且执行规则一致