pokemonlei

陈磊的博客 | pokemonlei

UE4GM系统

UE4的GM系统是基于UCheatManager开发的

1.处理GM的流程图

上图是客户端的处理流程,DS服务器上的处理流程大致类似,只不过玩家在DS上是一个NetConnection,所以PlayerController中保存的UPlayer的派生不是ULocalPlayer,而是UNetConnection。

2. 启用GM

默认情况下,GM只会在(GetNetMode() == NM_Standalone || GIsEditor)情况下才会启用,也就是说只要不是单机也不是编辑器下运行,默认是不会启动的,需要在PlayerController里手动调用EnableCheats(),此时只要构建类型不是SHIPPING和TEST,就会启用GM。当然也可以根据需要直接调用AddCheats(true)不论何时都启用。

3.如何添加自己的GM指令

添加自己的指令也比较简单,通过继承UCheatManager来实现自己的派生类即可,自己的GM类里实现具体的GM指令,方式如下:

1
2
UFUNCTION(exec,BlueprintCallable,Category="Cheat Manager")
void MyGMName(arglist);

其中UFUNCTION中的exec是必须的。

写完自己的GM类后,需要设置PlayerController中的CheatClass为自己的GM类,之后直接在控制台输入 MyGMName args 即可调用到自己的GM实现。

4.一个可在客户端远程调用DS执行GM的方案

由于有一些GM,比如添加物品,要在DS服务器上调用然后同步结果到客户端,而我们又不能直接在服务器上输入只能在客户端中输入,那就需要一种方式将输入的GM传给服务器。

这里用到的就是两个:UFUNCTION(exec) 和 UFUNCTION(Reliable, Server, WithValidation, BlueprintCallable) 函数,比如
UFUNCTION(exec)
void ServerGM(const FString& cmd);

UFUNCTION(Reliable, Server, WithValidation, BlueprintCallable)
virtual void ServerGM_RPC(const FString& cmd);

其中ServerGM用来供客户端执行,在函数内通过ServerGM_RPC将客户端想要执行的GM命令和参数同步给服务器来执行。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void AMyPlayerController::ServerGM(const FString& cmd)
{
//下面是从引擎源码的UPlayer::ConsoleCommand中抄过来的一段代码,大概作用是使用 | 分隔符处理多段的GM指令
const int32 CmdLen = cmd.Len();
TCHAR* CommandBuffer = (TCHAR*)FMemory::Malloc((CmdLen + 1) * sizeof(TCHAR));
TCHAR* Line = (TCHAR*)FMemory::Malloc((CmdLen + 1) * sizeof(TCHAR));

const TCHAR* Command = CommandBuffer;
FCString::Strcpy(CommandBuffer, (CmdLen + 1), *cmd.Left(CmdLen));

// 使用'|'分隔符处理多段GM指令
while (FParse::Line(&Command, Line, CmdLen + 1))
{
ServerGM_RPC(FString(Line));
}

FMemory::Free(CommandBuffer);
CommandBuffer = nullptr;

FMemory::Free(Line);
Line = nullptr;
}

void AMyPlayerController::ServerGM_RPC_Implementation(const FString& cmd)
{
ConsoleCommand(cmd, true);
}

此时如果3中的MyGMName需要在服务器上运行,那客户端执行:ServerGM MyGMName args 即可。
整个流程大概是:

  1. 先按文章开头的照流程图在客户端中执行GM,流程图最后一步UPlayer::Exec会在PlayerController上执行ProcessConsoleExec(),从而调用PlayerController的ServerGM函数,并把MyGMName args当做字符串参数传进去。
  2. ServerGM里一顿操作后,通过RPC,在服务器上调用ServerGM_RPC并将字符串MyGMName args 作为参数
  3. ServerGM_RPC直接调用ConsoleCommand,即直接走到了流程图中的这一步
  4. 之后就是按照流程图中,调用UPlayer派生类UNetConnection的Exec,然后走到我们自己的CheatManager,调用MyGMName