Unreal/Network
cadion
2024. 5. 21. 00:05
2024. 5. 21. 00:05
Server - Client Model
Server ( authority )
check request
replicate to client
Client ( clone )
function ( input, logic, etc.. ) → request for server
Init
bReplicates
SetIsReplicated(bool)
Request Function to Server ( RPCs remote procedure calls )
header
header
UFUNCTION ( server, reliable, validation )
type funcName(var);
cpp
cpp
.AddBind( param, object, funcName );
type funcName_Implementation(var)
{ 실행문 ( 서버에서 ) }
bool funcName_Validation(var)
{ return 유효성 } // false일 경우, cheating 규칙 적용
Unreal network model에서 권한의 단위는 Actor
Simulated Proxy Actor ( in client )
서버로 부터 받는 정보에 전적으로 의존
정보들을 종합하여 지역적(시간축) 상태에 대해 시뮬레이션 → 적용
이후 리플리케이트를 적용할 때, 시뮬레이션된 값과 서버로부터 새로 받은 정보를 기반으로 비교, 검정, 보간
Autonomous Proxy
Simulated Proxy에 더해, 유저의 입력을 포함하고 있으며, 역으로 server에게의 request를 포함
Role / RemoteRole
체크하는 당사자 ( server or client 등 ) 입장에서, 자신의 정보 - Role / 비교대상의 정보 - Remote Role
Actor.Role property를 통해 접근가능
Autonomous Proxy는, 자기자신에 대한 결정권은 자신이 지니므로
Local에서 수행 후
Server 에서 UFUNCTION(server)로 할당된 로직을 수행하는 형태로 구현되어야 함
동기화를 고려하면, local 수행용 함수와, server 수행용 implementation 함수는 동일한 로직이어야 함
Replicate Property
Object 단위
bReplicates = true; ( Object ) ( constructor )
UPROPERTY(Replicated) [property]
void AActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const // header에서 선언할 필요 없음 ( property의 속성에 replicated를 통해 선언 )
{ DOREPLIFETIME(AActor, [property]); }
( ReplicatedUsing = [함수명] )
해당 함수를 통해 replicate 진행
같은 class 내의 UFUNCTION 항목?
[함수명](OnRep_Func)은 property 값이 변경될마다, client ( has authority = false )에서만 이루어짐
header
UPROPERTY(ReplicatedUsing = OnRep_ReplicatedTransform)
FTransform ReplicatedTransform;
cpp
void AMyKart::OnRep_ReplicatedTransform()
{
SetActorTransform(ReplicatedTransform);
}
Actor::NetUpdateFrequency ( sec, beginPlay )
owner player controller와 현재 접속한 instance에게 할당된 player controller를 비교해서 IsLocallyControlled 체크하는듯?
hasAuthority는 "해당 액터"의 속성에 관한 것 ( 호출자하고는 별개인듯? )
HasAuthority() 체크시, 함수를 포함하는 actor instance가 아니라 함수를 호출하는 actor instance의 authority를 체크하는듯?
IsLocallyControlled() 체크시, 함수를 포함하는 actor instance == 함수를 호출하는 actor instance 체크하는듯?
canonical state ( 표준 상태 ) - 프로토콜 -> 서버-클라 유효성검정을 위한 데이터들의 모듈화된 군집 ( serialize? or structure? )APawn::IsLocallyControlled() { return APawn::Controller(allocated) && Controller->IsLocalController() } Actor::Role
AActor::HasAuthority() { return GetLocalRole }
보간 패턴
보간 패턴
Location - Linear interp.
Revolutionary - Location Slerp ( spherical linrea interpolation )
Velocity
exponential 합성하여 사용
sigmoid interp.
bezier ( hermite ) interp. - FMath::CubicInterp ( p0, t0, p1, t1, a ) - position, tangent(velocity or pos.derivation), alpha
Acceleration
Editor에서 소켓 접속 방법
서버
"${언리얼 엔진 경로}\Engine\Binaries\Win64\UnrealEditor.exe" "${프로젝트 경로}\${프로젝트이름}.uproject" /Game/{맵 경로}?listen -server -log
로그 확인 필요사항
포트번호 확인 ( listening on port로 검색 )
created socket for bind adress : 0.0.0.0 이라면, 모든 ip의 접근에 대해 허가된 상태
클라이언트
"${언리얼 엔진 경로}\Engine\Binaries\Win64\UnrealEditor.exe" "${프로젝트 경로}\${프로젝트이름}.uproject" 서버ip:포트번호 -game -log
cadion
2024. 5. 21. 00:02
2024. 5. 21. 00:02
Krazy Kart Sample
UGoKartMovementComponent : UActorComponent
Tick → !HasAuthority? LastMove = CreateMove() → SimulateMove(LastMove) :
Create Move(DeltaTime) ( → 현재 값을 FGoKartMove instance에 넣음 )
SimulateMove(FGoKartMove&)
Throttle, MaxDrivingForce, ActorForwardVector, AirResistance, RollingResistance, Mass, DeltaTime으로 → ApplyRotation, UpdateLocationFromVelocity // 실제 로컬에 적용하는 물리값
GetAirResistance() // 속도기반 공기저항 계산
GetRollingResistance() // 속도/중량기반 구르기 마찰저항 계산
ApplyRotation() ( ← 서버가 아닐떄 ) ( → Velocity 및 Rotation actor에 적용 )
UpdateLocationFromVelocity(DeltaTime) ( ← 서버가 아닐때 ) ( → Velocity로 Translation 적용 )
AGokart : APawn
SetupPlayerInputComponent(arg) ( → bind 앞,옆 이동 )
MoveForward(value) ( ← 키바인딩 ) ( → Throttle 값 변경 )
MoveRight(value) ( ← 키바인딩 ) ( → SteeringThrow값 변경 )
UGoKartMovementReplicator : UActorComponent
Constructor ( → bCanEverTick = true, SetIsReplicated(true) )
Tick
자율프록시
MovementComponent로부터 LastMove를 얻어서, UnacknowledgedMoves에 추가
LastMove를 서버로 보냄
remote role = 시뮬프록시 ( → UpdateServerState() )
시뮬프록시 ( → ClientTick() )
UpdateServerState(FGoKartMove) ( ← remote role이 시뮬프록시일떄 ) ( → FServerState instance를 받은 move와 // 현재 actor state값 복사 )
ClientTick(DeltaTime) ( ← 시뮬프록시 ) ( → 시작Loc(클라위치), 타겟Loc(서버위치), 시작속도(클라속도), 타겟속도(서버속도), 속도Derivative 5개param으로 HermiteCubicSpline 써서 보간 )
FMath::CubicInterp ( loc )
FMath::CubicInterpDerivative ( vel )
GetLifetimeReplicatedProps ( ← 프로퍼티 복제 ) ( → FServerState )
OnRep_ServeState() ( → 복제 받을시 호출 RPC )
자율프록시() ( → serverstate의 트랜스폼, 벨로시티적용 / acknowledgeMove 갱신 ( ClearAcknowledgeMoves )
ClearAcknowledgeMoves = 각 FMove의 적용시간 비교해서, last무브 이후것만 남김
MoveComponent.Simulate ( 남은 FMove 적용, 시뮬레이션 해서 위치 덮어쓰기 )
Puzzle Platform Sample
MenuSystem
UMenuWidget : UUserWidget // 메뉴리스트 프레임
Setup() ( → 기본변수 셋업, SetInputMode(FInputModeUIOnly), bShowMouseCursor = true )
Teardown() ( → remove from viewport || remove from parent, SetInputMode, bShowMouseCursor ) // 자가붕괴
SetMenuInterface() ( → 참조변수_MenuInterface 지정 )
UInGameMenu : UMenuWidget // level에서 esc 메뉴
Init() ( → 버튼에 함수 바인딩 )
QuitPressed() ( → teardown 및 LoadMainMenu() ( main menu level로 이동 ) )
Inherit
Teardown() // 자가붕괴
LodaMainMenu()
UMainMenu : UMenuWidget // 로비 메뉴
Constructor ( → BP클래스객체 = ConstructorHelpers::FClassFind<UUserWidget> BPClass(TEXT).Class )
Init ( → 버튼 바인딩 )
OpenMainMenu() ( → menuswitcher(U위젯스위처).SetActiveWidget(타겟자식위젯 or 인덱스)
HostServer() ( → MenuInterface ( UEditableTextBlock위젯.Text.Tostring )
SelectIndex ( int ) ( → int 번호 var에 저장 후, UpdateChildren() )
UpdateChildren() ( → var에 저장된 int번호기준, 선택된거 "선택상태", 나머지 "비선택상태"로 변경 bool UServerRow.Selected)
OpenJoinMenu() ( → 위젯스위처에서 join메뉴 활성, RefreshServerList() )
OpenMainMenu() (→ 위젯스위처에서 MainMenu 활성 )
QuitPressed() ( → ConsoleCommand("quit") )
Inherit (MenuWidget)
IMenuInterface : UInterface
Host(FString)
Join(uint)
LoadMainMenu()
RefreshServerList()
UServerRow : UUserWidget // 메뉴리스트 컴포넌트
Setup ( 부모ref, 자기 번호(부모안에서) ) ( ← 부모에서 생성 후 호출 ) ( → ref들 내부변수로 할당 및 Rowbutton 바인딩 )
OnClciekd() ( → 부모의 선택됨인덱스에 자기한테 부여된 번호 주입 )
LobbyGameMode :: AGameModeBase
StartGame() ( → GameInstance.StartSession // World.ServerTravel )
inherit ( game mode )
PostLogin ( APlayerController ) ( → 인원수 체크 / 다모이면 StartGame() )
Logout ( APlayerController ) ( → 인원수 감소 )
PP.GameInstance : UGameInstance, IMenuInterface
Function
Init()
LoadMenuWidget() ( Menu ← MenuClass // Menu.Setup // Menu.SetMenuInterface(this) ) // 인터페이스를 통함
InGameLoadMenu() ( InGameLoadMenu ← InGameLoadMenuClass // InGameLoadMenu.Setup // InGameLoadMenu.SetMenuInterface(this) ) // 인터페이스를 통함
StartSession() ( → SessionInterface.StartSession(세션이름) )
CreateSession() ( → SessionInterface.CreateSession( 호스팅플레이어num, 원하는세션이름, FOnlineSessionSettings )
OnCreateSessionComplete(FName SessionName, bool Success) ( ← SessionInterface.OncreateSessionCompleteDelegates.Add ) when Init ( → Menu.Teardown() // World.ServerTravel(레벨파일경로?listen) )
OnDestroySessionComplete(FName SessionName, bool Success) ( ← SessionInterface.OnDestoySessionCompleteDelegates.Add ) when Init
OnFindSessionsComplete(bool Success) ( ← SessionInterface.OnFindSessionsCompleteDelegates.Add ) when Init
OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) ( ← OnJoinSessionCompleteDelegates ) when Init ( → GetFirstLocalPlayerController로 자기 PC 얻고 // PC.ClientTravel(ip주소, ETravelType )
OnNetworkFailure ( UWorld* World, UNetDriver* NetDriver, ENetworkFailure::Type FailureType, const FString& ErrorString ) ( ← GEngine.OnNetworkFailure().Add )
Inherit ( Interface )
Host(FString) ( → 원하는 세션이름 받고(arg) // 세션있으면 기존세션 제거 // 없으면 CreateSession() )
Join(uint) ( → Menu 끄고 // SessionInterface.JoinSession(유저번호, 세션이름, FOnlineSessionSearchResult ( session search시 참조로 받아올 수 있는 structure )
LoadMainMenu() ( → PC.ClientTravel() )
RefreshServerList() ( → SessionInterface.FindSession(탐색플레이어num, TSharedRef<FOnlineSessionSearch>)
Property
TSubclassOf<UUserWidget> MenuClass ( ← ContructorHelpers::FClassFinde<UUserWidget> MenuBPClass(TEXT(경로)) + MenuBPClass.class ) when Construct
TSubclassOf<UUserWidget> InGameMenuClass ( ← ContructorHelpers::FClassFinde<UUserWidget> InGameMenuBPClass(TEXT(경로)) + InGameMenuBPClass.class ) when Construct
MainMenu* Menu
IOnlinSessionPtr SessionInterface
TSharedPtr<FOnlineSessionSearch> SessionSearch
FString DesiredServerName
PP.GameMode
DefaultEngine.ini
[/Script/EngineSettings.GameMapsSettings]
TransitionMap =/Game/PuzzlePlatforms/Maps/Transiton.Transiton ( seamless travel을 위한 map 설정 )
[/Script/Engine.GameEngine]
+ NetDriverDefinitions =( DefName = " GameNetDriver" , DriverClassName = " OnlineSubsystemSteam.SteamNetDriver" , DriverClassNameFallback = " OnlineSubsystemUtils.IpNetDriver" )
[OnlineSubsystem]
DefaultPlatformService =Steam
[OnlineSubsystemSteam]
bEnabled =true
SteamDevAppId =480
[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName = " OnlineSubsystemSteam.SteamNetConnection"
Projectname.uproject
"Plugins": [ { "Name": "OnlineSubsystemSteam", "Enabled": true } ]
Projectname.Build.cs
PublicDependencyModuleNames.AddRange( "OnlineSubsystemStea" )
cadion
2024. 5. 21. 00:00
2024. 5. 21. 00:00