RPCs는 보내진 객체와 같은 객체에 도착하는 통신이다.
ServerRpc, ObserversRpc, TargetRpc 세 종류가 있다.
RPCs는 객체에 종속되어 있기 때문에, NetworkBehaviour를 상속받은 스크립트에서만 호출 가능하다.
private void Update()
{
//If owner and space bar is pressed.
if (base.IsOwner && Input.GetKeyDown(KeyCode.Space))
RpcSendChat("Hello world, from owner!");
}
[ServerRpc]
private void RpcSendChat(string msg)
{
Debug.Log($"Received {msg} on the server.");
}
[ServerRpc(RequireOwnership = false)]
private void RpcSendChat(string msg, NetworkConnection conn = null)
{
Debug.Log($"Received {msg} on the server from connection {conn.ClientId}.");
}
Network Observer 컴포넌트를 사용하여 설정한다.ObserverRpc attribute를 붙여야 한다.private void FixedUpdate()
{
RpcSetNumber(Time.frameCount);
}
[ObserversRpc]
private void RpcSetNumber(int next)
{
Debug.Log($"Received number {next} from the server.");
}
ObserverRpc attribute의 BufferLast를 true로 설정하면 ObserversRpc를 통해 전송된 최신 값들이, 새로 접속한 클라에게도 자동으로 전달되도록 설정할 수 있다.[ObserversRpc(ExcludeOwner = true, BufferLast = true)]
private void RpcSetNumber(int next)
{
//This won't run on owner and will send to new clients.
Debug.Log($"Received number {next} from the server.");
}
(owner에는 안 보내고 새 클라에 보내는 예제)
TargetRpc attribute를 붙이면 된다.NetworkConnection이어야 한다. 이 연결이 데이터가 가는 곳이다.private void UpdateOwnersAmmo()
{
/* Even though this example passes in owner, you can send to
* any connection that is an observer. */
RpcSetAmmo(base.Owner, 10);
}
[TargetRpc]
private void RpcSetAmmo(NetworkConnection conn, int newAmmo)
{
//This might be something you only want the owner to be aware of.
_myAmmo = newAmmo;
}
[ObserversRpc][TargetRpc]
private void DisplayChat(NetworkConnection target, string sender, string message)
{
Debug.Log($"{sender}: {message}."); //Display a message from sender.
}
[Server]
private void SendChatMessage()
{
//Send to only the owner.
DisplayChat(base.Owner, "Bob", "Hello world");
//Send to everyone.
DisplayChat(null, "Bob", "Hello world");
}
Channeltype을 rpc의 마지막 인자로 넣어서 reliable 또는 unreliable하게 호출 가능하다.private bool _reliable;
private void FixedUpdate()
{
//Reliable or unreliable can be switched at runtime!
_reliable = !_reliable;
Channel channel = (_reliable) ? Channel.Reliable : Channel.Unreliable;
RpcTest("Anything", channel);
}
/* This example uses ServerRpc, but any RPC will work.
* Although this example shows a default value for the channel,
* you do not need to include it. */
[ServerRpc]
private void RpcTest(string txt, Channel channel = Channel.Reliable)
{
if (channel == Channel.Reliable)
Debug.Log("Message received! I never doubted you.");
else if (channel == Channel.Unreliable)
Debug.Log($"Glad you got here, I wasn't sure you'd make it.");
}
/* Example of using Channel with a ServerRpc that
* also provides the calling connection. */
[ServerRpc(RequireOwnership = false)]
private void RpcTest(string txt, Channel channel = Channel.Reliable, NetworkConnection sender = null)
{
Debug.Log($"Received on channel {channel} from {sender.ClientId}.");
}
//Keeping in mind all RPC types can use RunLocally.
[ServerRpc(RunLocally = true)]
private void RpcTest()
{
//This debug will print on the server and the client calling the RPC.
Debug.Log("Rpc Test!");
}
//This implies we know the data will never be larger than 3500 bytes.
//If the data is larger then a resize will occur resulting in garbage collection.
[ServerRpc(DataLength = 3500)]
private void ServerSendBytes(byte[] data) {}
OnDestroy 이전에 초기화되므로, 거기서 접근 못 함.Awake에서 SyncType 변경하면 서버와 클라 모두에 영향 미쳐서 별도 싱크 필요 없음. 초기화 이후에 싱크 맞추고 싶으면 OnStartServer 사용.클라와 서버를 동일 빌드에서 돌리면 약간의 제한이 발생함.
호스트 클라일 때 asServer 인자가 false면 이전값이 null이나 현재값으로 찍힐 수 있다.
// This example assumes you are acting as the host client.
// We will imagine previous value was 10, and next is 20.
private void _mySyncVar_OnChange(int prev, int next, bool asServer)
{
// Only run the logic using the previous value if being called
// with asServer true, or if only the client is started.
if (asServer || base.IsClientOnlyStarted)
DoSomething(prev, next);
}
bool IsValidXY(int& R, int& C, vector<string>& lake, int& newx, int& newy)
{
if (0 <= newx && newx < R && 0 <= newy && newy < C) return true;
else return false;
}
void B3197::Solution()
{
fastio;
// 얼음 녹인 뒤에 오리 한 쪽에서부터 갈 수 있는지 체크.
// 근데 이전 체크에서 얼음이었던 곳에서부터 bfs 다시 시작함.
int R, C;
string s;
cin >> R >> C;
vector<string> lake(R);
pii duck;
queue<pii> iceq;
vector<vector<bool>> visited(R, vector<bool>(C));
vector<vector<bool>> willmelt(R, vector<bool>(C));
for (int i = 0; i < R; i++)
cin >> lake[i];
vector<int> dx = { 1,-1,0,0 };
vector<int> dy = { 0,0,1,-1 };
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
char c = lake[i][j];
if (c == 'X') {
for (int k = 0; k < 4; k++) {
int newx = i + dx[k]; int newy = j + dy[k];
if (IsValidXY(R, C, lake, newx, newy)
&& lake[newx][newy] == '.') {
iceq.push({ i,j });
willmelt[i][j] = true;
break;
}
}
}
else if (c == 'L') {
duck = { i,j };
}
}
}
bool done = false;
int day = 0;
queue<pii> duckq;
queue<pii> nextdq;
duckq.push(duck);
visited[duck.first][duck.second] = true;
while (true)
{
while (!nextdq.empty()) {
duckq.push(nextdq.front()); nextdq.pop();
}
while (!duckq.empty()) {
pii d = duckq.front(); duckq.pop();
for (int i = 0; i < 4; i++) {
int newx = d.first + dx[i];
int newy = d.second + dy[i];
if (IsValidXY(R, C, lake, newx, newy)
&& !visited[newx][newy])
{
if (lake[newx][newy] == '.') {
visited[newx][newy] = true;
duckq.push({ newx,newy });
}
else if (lake[newx][newy] == 'X'
&& willmelt[newx][newy]) {
visited[newx][newy] = true;
nextdq.push({ newx, newy });
}
else if (lake[newx][newy] == 'L') {
done = true;
break;
}
}
}
if (done) break;
}
if (done) break;
int iceqlen = iceq.size();
while (iceqlen--) {
pii ice = iceq.front(); iceq.pop();
lake[ice.first][ice.second] = '.';
for (int i = 0; i < 4; i++) {
int newx = ice.first + dx[i];
int newy = ice.second + dy[i];
if (IsValidXY(R, C, lake, newx, newy)
&& !willmelt[newx][newy]
&& lake[newx][newy] == 'X')
{
willmelt[newx][newy] = true;
iceq.push({ newx,newy });
}
}
}
day++;
}
cout << day;
}
45%에서 시간 초과. 내일 다시.