Files
a301_mmo_game_server/ClientTester/EchoClientTester/DummyService/DummyClients.cs
2026-03-04 10:02:48 +09:00

210 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Concurrent;
using System.Diagnostics;
using ClientTester.Packet;
using LiteNetLib;
using LiteNetLib.Utils;
using Serilog;
namespace ClientTester.DummyService;
public class DummyClients
{
private NetManager manager;
private EventBasedNetListener listener;
private NetDataWriter? writer;
public NetPeer? peer;
public long clientId;
// seq → 송신 타임스탬프 (Stopwatch tick)
private ConcurrentDictionary<int, long> pendingPings = new();
private int seqNumber;
private const int MAX_PENDING_PINGS = 1000;
// info
private Vector3 position = new Vector3();
private int rotY = 0;
private float moveSpeed = 3.5f;
private float distance = 0.0f;
private float preTime = 0.0f;
// 이동 계산용
private static readonly Random random = new();
private readonly Stopwatch moveClock = Stopwatch.StartNew();
private float posX = 0f;
private float posZ = 0f;
private float dirX = 0f;
private float dirZ = 0f;
// 유닛 테스트용 (0 = 제한 없음)
public int TestCount
{
get;
set;
} = 0;
// 통계
public int SentCount
{
set;
get;
}
public int ReceivedCount
{
set;
get;
}
public double LastRttMs
{
set;
get;
}
public double TotalRttMs
{
set;
get;
}
public int RttCount
{
set;
get;
}
public DummyClients(long clientId, string ip, int port, string key)
{
this.clientId = clientId;
listener = new EventBasedNetListener();
manager = new NetManager(listener);
writer = new NetDataWriter();
listener.PeerConnectedEvent += netPeer =>
{
peer = netPeer;
Log.Information("[Client {ClientId:00}] 연결됨", this.clientId);
// clientID가 토큰의 hashKey라고 가정함
PacketHeader packetHeader = new PacketHeader();
packetHeader.Code = (PacketCode)1;
packetHeader.BodyLength = 4 + sizeof(long);
writer.Put(clientId);
peer.Send(writer, DeliveryMethod.ReliableOrdered);
};
listener.NetworkReceiveEvent += (peer, reader, channel, deliveryMethod) =>
{
short code = reader.GetShort();
short bodyLength = reader.GetShort();
string? msg = reader.GetString();
long sentTick;
if (msg != null && msg.StartsWith("Echo seq:") &&
int.TryParse(msg.Substring("Echo seq:".Length), out int seq) &&
pendingPings.TryRemove(seq, out sentTick))
{
double rttMs = (Stopwatch.GetTimestamp() - sentTick) * 1000.0 / Stopwatch.Frequency;
LastRttMs = rttMs;
TotalRttMs += rttMs;
RttCount++;
}
ReceivedCount++;
if (TestCount > 0 && ReceivedCount >= TestCount)
{
peer.Disconnect();
}
reader.Recycle();
};
listener.PeerDisconnectedEvent += (peer, info) =>
{
Log.Warning("[Client {ClientId:00}] 연결 끊김: {Reason}", this.clientId, info.Reason);
this.peer = null;
};
manager.Start();
manager.Connect(ip, port, key);
}
public void UpdateDummy()
{
// 델타 타임 계산 (초 단위)
float now = (float)moveClock.Elapsed.TotalSeconds;
float delta = preTime > 0f ? now - preTime : 0.1f;
preTime = now;
// 남은 거리가 없으면 새 방향·목표 거리 설정
if (distance <= 0f)
{
// 현재 각도에서 -30~+30도 범위로 회전
rotY = (rotY + random.Next(-30, 31) + 360) % 360;
float rad = rotY * MathF.PI / 180f;
dirX = MathF.Sin(rad);
dirZ = MathF.Cos(rad);
// 3초~12초에 도달할 수 있는 거리 = moveSpeed × 랜덤 초
float seconds = 3f + (float)random.NextDouble() * 9f;
distance = moveSpeed * seconds;
}
// 이번 틱 이동량 (남은 거리 초과 방지)
float step = MathF.Min(moveSpeed * delta, distance);
posX += dirX * step;
posZ += dirZ * step;
distance -= step;
// 정수 Vector3 갱신
position.X = (int)MathF.Round(posX);
position.Z = (int)MathF.Round(posZ);
}
public void SendTransform()
{
if (peer == null || writer == null)
{
return;
}
UpdateDummy();
int seq = seqNumber++;
pendingPings[seq] = Stopwatch.GetTimestamp();
TransformPlayerPacket transformPlayerPacket = new TransformPlayerPacket
{
PlayerId = (int)clientId,
RotY = rotY,
Position = new Packet.Vector3
{
X = position.X,
Y = 0, // 높이는 버린다.
Z = position.Z
}
};
// Protobuf 직렬화 + 헤더 조립
byte[] data = PacketSerializer.Serialize((ushort)PacketCode.TRANSFORM_PLAYER, transformPlayerPacket);
writer.Put(data);
// 이동은 손실 감수함
peer.Send(writer, DeliveryMethod.Unreliable);
SentCount++;
writer.Reset();
}
public double AvgRttMs => RttCount > 0 ? TotalRttMs / RttCount : 0.0;
public void PollEvents()
{
manager.PollEvents();
}
public void Stop()
{
manager.Stop();
}
}