first commit
This commit is contained in:
18
ClientTester/EchoClientTester/EchoClientTester.csproj
Normal file
18
ClientTester/EchoClientTester/EchoClientTester.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>ClientTester</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LiteNetLib" Version="2.0.2" />
|
||||
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,107 @@
|
||||
using LiteNetLib.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace ClientTester.EchoDummyService;
|
||||
|
||||
public class DummyClientService
|
||||
{
|
||||
private readonly List<DummyClients> _clients;
|
||||
private readonly int _sendInterval;
|
||||
|
||||
public DummyClientService(int count, string ip, int port, string key, int sendIntervalMs = 1000)
|
||||
{
|
||||
_sendInterval = sendIntervalMs;
|
||||
_clients = Enumerable.Range(0, count)
|
||||
.Select(i => new DummyClients(i, ip, port, key))
|
||||
.ToList();
|
||||
|
||||
Log.Information("[SERVICE] {Count}개 클라이언트 생성 → {Ip}:{Port}", count, ip, port);
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken ct)
|
||||
{
|
||||
await Task.WhenAll(
|
||||
PollLoopAsync(ct),
|
||||
SendLoopAsync(ct)
|
||||
);
|
||||
}
|
||||
|
||||
private async Task PollLoopAsync(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
foreach (var c in _clients)
|
||||
c.PollEvents();
|
||||
|
||||
try { await Task.Delay(15, ct); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendLoopAsync(CancellationToken ct)
|
||||
{
|
||||
try { await Task.Delay(500, ct); }
|
||||
catch (OperationCanceledException) { return; }
|
||||
|
||||
int tick = 0;
|
||||
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
int sent = 0;
|
||||
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
client.SendPing();
|
||||
if (client.Peer != null) sent++;
|
||||
}
|
||||
|
||||
Log.Information("[TICK {Tick:000}] {Sent}/{Total} 전송", tick, sent, _clients.Count);
|
||||
tick++;
|
||||
|
||||
try { await Task.Delay(_sendInterval, ct); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintStats()
|
||||
{
|
||||
int totalSent = 0, totalRecv = 0;
|
||||
int connected = 0;
|
||||
|
||||
Log.Information("════════════ Performance Report ════════════");
|
||||
|
||||
double totalAvgRtt = 0;
|
||||
|
||||
foreach (var c in _clients)
|
||||
{
|
||||
var stats = c.Peer?.Statistics;
|
||||
long loss = stats?.PacketLoss ?? 0;
|
||||
float lossPct = stats?.PacketLossPercent ?? 0f;
|
||||
|
||||
Log.Information(
|
||||
"[Client {ClientId:00}] Sent={Sent} Recv={Recv} | Loss={Loss}({LossPct:F1}%) AvgRTT={AvgRtt:F3}ms LastRTT={LastRtt:F3}ms",
|
||||
c.ClientId, c.SentCount, c.ReceivedCount, loss, lossPct, c.AvgRttMs, c.LastRttMs);
|
||||
|
||||
totalSent += c.SentCount;
|
||||
totalRecv += c.ReceivedCount;
|
||||
totalAvgRtt += c.AvgRttMs;
|
||||
if (c.Peer != null) connected++;
|
||||
}
|
||||
|
||||
double avgRtt = connected > 0 ? totalAvgRtt / connected : 0;
|
||||
|
||||
Log.Information("────────────────────────────────────────────");
|
||||
Log.Information(
|
||||
"[TOTAL] Sent={Sent} Recv={Recv} Connected={Connected}/{Total} AvgRTT={AvgRtt:F3}ms",
|
||||
totalSent, totalRecv, connected, _clients.Count, avgRtt);
|
||||
Log.Information("════════════════════════════════════════════");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
foreach (var c in _clients)
|
||||
c.Stop();
|
||||
|
||||
Log.Information("[SERVICE] 모든 클라이언트 종료됨.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Diagnostics;
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace ClientTester.EchoDummyService;
|
||||
|
||||
public class DummyClients
|
||||
{
|
||||
public NetManager Manager;
|
||||
public EventBasedNetListener Listener;
|
||||
public NetPeer? Peer;
|
||||
public int ClientId;
|
||||
|
||||
// seq → 송신 타임스탬프 (Stopwatch tick)
|
||||
private readonly Dictionary<int, long> _pendingPings = new();
|
||||
private int _seqNumber;
|
||||
|
||||
// 통계
|
||||
public int SentCount;
|
||||
public int ReceivedCount;
|
||||
public double LastRttMs;
|
||||
public double TotalRttMs;
|
||||
public int RttCount;
|
||||
|
||||
public DummyClients(int clientId, string ip, int port, string key)
|
||||
{
|
||||
ClientId = clientId;
|
||||
Listener = new EventBasedNetListener();
|
||||
Manager = new NetManager(Listener);
|
||||
|
||||
Listener.PeerConnectedEvent += peer =>
|
||||
{
|
||||
Peer = peer;
|
||||
Log.Information("[Client {ClientId:00}] 연결됨", ClientId);
|
||||
};
|
||||
|
||||
Listener.NetworkReceiveEvent += (peer, reader, channel, deliveryMethod) =>
|
||||
{
|
||||
var msg = reader.GetString();
|
||||
|
||||
// "ack:seq:{seqNum}" 파싱
|
||||
var parts = msg.Split(':');
|
||||
if (parts.Length == 3 && parts[0] == "ack" && parts[1] == "seq"
|
||||
&& int.TryParse(parts[2], out int seq)
|
||||
&& _pendingPings.TryGetValue(seq, out long sentTick))
|
||||
{
|
||||
double rttMs = (Stopwatch.GetTimestamp() - sentTick)
|
||||
* 1000.0 / Stopwatch.Frequency;
|
||||
LastRttMs = rttMs;
|
||||
TotalRttMs += rttMs;
|
||||
RttCount++;
|
||||
_pendingPings.Remove(seq);
|
||||
}
|
||||
|
||||
ReceivedCount++;
|
||||
reader.Recycle();
|
||||
};
|
||||
|
||||
Listener.PeerDisconnectedEvent += (peer, info) =>
|
||||
{
|
||||
Log.Warning("[Client {ClientId:00}] 연결 끊김: {Reason}", ClientId, info.Reason);
|
||||
Peer = null;
|
||||
};
|
||||
|
||||
Manager.Start();
|
||||
Manager.Connect(ip, port, key);
|
||||
}
|
||||
|
||||
public void SendPing()
|
||||
{
|
||||
if (Peer is null) return;
|
||||
|
||||
int seq = _seqNumber++;
|
||||
_pendingPings[seq] = Stopwatch.GetTimestamp();
|
||||
|
||||
var writer = new NetDataWriter();
|
||||
writer.Put($"seq:{seq}");
|
||||
Peer.Send(writer, DeliveryMethod.ReliableOrdered);
|
||||
SentCount++;
|
||||
}
|
||||
|
||||
public double AvgRttMs => RttCount > 0 ? TotalRttMs / RttCount : 0.0;
|
||||
|
||||
public void PollEvents() => Manager.PollEvents();
|
||||
|
||||
public void Stop() => Manager.Stop();
|
||||
}
|
||||
604
ClientTester/EchoClientTester/Packet/PacketBody.cs
Normal file
604
ClientTester/EchoClientTester/Packet/PacketBody.cs
Normal file
@@ -0,0 +1,604 @@
|
||||
using ProtoBuf;
|
||||
|
||||
namespace ClientTester.Packet;
|
||||
|
||||
// ============================================================
|
||||
// 공통 타입
|
||||
// ============================================================
|
||||
|
||||
[ProtoContract]
|
||||
public class Vector3
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public float X
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public float Y
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public float Z
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class PlayerInfo
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public string Nickname
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int Level
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int Hp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(5)]
|
||||
public int MaxHp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(6)]
|
||||
public int Mp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(7)]
|
||||
public int MaxMp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(8)]
|
||||
public Vector3 Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(9)]
|
||||
public float RotY
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class ItemInfo
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int ItemId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int Count
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 인증
|
||||
// ============================================================
|
||||
|
||||
// RECV_TOKEN
|
||||
[ProtoContract]
|
||||
public class RecvTokenPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public string Token
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// LOAD_GAME
|
||||
[ProtoContract]
|
||||
public class LoadGamePacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public bool IsAccepted
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public PlayerInfo Player
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 로비
|
||||
// ============================================================
|
||||
|
||||
// INTO_LOBBY
|
||||
[ProtoContract]
|
||||
public class IntoLobbyPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public List<PlayerInfo> Players
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// EXIT_LOBBY
|
||||
[ProtoContract]
|
||||
public class ExitLobbyPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 인스턴스 던전
|
||||
// ============================================================
|
||||
|
||||
public enum BossState
|
||||
{
|
||||
START,
|
||||
END,
|
||||
PHASE_CHANGE
|
||||
}
|
||||
|
||||
public enum BossResult
|
||||
{
|
||||
SUCCESS,
|
||||
FAIL
|
||||
}
|
||||
|
||||
// INTO_INSTANCE
|
||||
[ProtoContract]
|
||||
public class IntoInstancePacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int InstanceId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int BossId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public List<int> PlayerIds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// UPDATE_BOSS
|
||||
[ProtoContract]
|
||||
public class UpdateBossPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public BossState State
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int Phase
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public BossResult Result
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} // END일 때만 유효
|
||||
}
|
||||
|
||||
// REWARD_INSTANCE
|
||||
[ProtoContract]
|
||||
public class RewardInstancePacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int Exp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public List<ItemInfo> Items
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// EXIT_INSTANCE
|
||||
[ProtoContract]
|
||||
public class ExitInstancePacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 파티
|
||||
// ============================================================
|
||||
|
||||
public enum PartyUpdateType
|
||||
{
|
||||
CREATE,
|
||||
DELETE
|
||||
}
|
||||
|
||||
public enum UserPartyUpdateType
|
||||
{
|
||||
JOIN,
|
||||
LEAVE
|
||||
}
|
||||
|
||||
// UPDATE_PARTY
|
||||
[ProtoContract]
|
||||
public class UpdatePartyPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PartyId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public PartyUpdateType Type
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int LeaderId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// UPDATE_USER_PARTY
|
||||
[ProtoContract]
|
||||
public class UpdateUserPartyPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PartyId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public UserPartyUpdateType Type
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 플레이어
|
||||
// ============================================================
|
||||
|
||||
public enum PlayerActionType
|
||||
{
|
||||
IDLE,
|
||||
MOVE,
|
||||
ATTACK,
|
||||
SKILL,
|
||||
DODGE,
|
||||
DIE,
|
||||
REVIVE
|
||||
}
|
||||
|
||||
// TRANSFORM_PLAYER
|
||||
[ProtoContract]
|
||||
public class TransformPlayerPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public Vector3 Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public float RotY
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ACTION_PLAYER
|
||||
[ProtoContract]
|
||||
public class ActionPlayerPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public PlayerActionType Action
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int SkillId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} // ATTACK, SKILL일 때
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int TargetId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} // 공격 대상
|
||||
}
|
||||
|
||||
// STATE_PLAYER
|
||||
[ProtoContract]
|
||||
public class StatePlayerPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int PlayerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int Hp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int MaxHp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int Mp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(5)]
|
||||
public int MaxMp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// NPC
|
||||
// ============================================================
|
||||
|
||||
public enum NpcActionType
|
||||
{
|
||||
IDLE,
|
||||
MOVE,
|
||||
ATTACK,
|
||||
SKILL,
|
||||
DIE
|
||||
}
|
||||
|
||||
// TRANSFORM_NPC
|
||||
[ProtoContract]
|
||||
public class TransformNpcPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int NpcId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public Vector3 Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public float RotY
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ACTION_NPC
|
||||
[ProtoContract]
|
||||
public class ActionNpcPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int NpcId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public NpcActionType Action
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int PatternId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} // 사용 패턴/스킬 번호
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int TargetId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// STATE_NPC
|
||||
[ProtoContract]
|
||||
public class StateNpcPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int NpcId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int Hp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int MaxHp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int Phase
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 데미지
|
||||
// ============================================================
|
||||
|
||||
// DAMAGE
|
||||
[ProtoContract]
|
||||
public class DamagePacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public int AttackerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(2)]
|
||||
public int TargetId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int Amount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ProtoMember(4)]
|
||||
public bool IsCritical
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
61
ClientTester/EchoClientTester/Packet/PacketHeader.cs
Normal file
61
ClientTester/EchoClientTester/Packet/PacketHeader.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace ClientTester.Packet;
|
||||
|
||||
public enum PacketCode
|
||||
{
|
||||
NONE,
|
||||
// 초기 클라이언트 시작시 jwt토큰 받아옴
|
||||
RECV_TOKEN,
|
||||
// jwt토큰 검증후 게임에 들어갈지 말지 (내 데이터도 전송)
|
||||
LOAD_GAME,
|
||||
|
||||
// 마을(로비)진입시 모든 데이터 로드
|
||||
INTO_LOBBY,
|
||||
|
||||
// 로비 나가기
|
||||
EXIT_LOBBY,
|
||||
|
||||
// 인스턴스 던전 입장
|
||||
INTO_INSTANCE,
|
||||
|
||||
// 결과 보상
|
||||
REWARD_INSTANCE,
|
||||
|
||||
// 보스전 (시작, 종료)
|
||||
UPDATE_BOSS,
|
||||
|
||||
// 인스턴스 던전 퇴장
|
||||
EXIT_INSTANCE,
|
||||
|
||||
// 파티 (생성, 삭제)
|
||||
UPDATE_PARTY,
|
||||
|
||||
// 파티 유저 업데이트(추가 삭제)
|
||||
UPDATE_USER_PARTY,
|
||||
|
||||
// 플레이어 위치, 방향
|
||||
TRANSFORM_PLAYER,
|
||||
|
||||
// 플레이어 행동 업데이트
|
||||
ACTION_PLAYER,
|
||||
|
||||
// 플레이어 스테이트 업데이트
|
||||
STATE_PLAYER,
|
||||
|
||||
// NPC 위치, 방향
|
||||
TRANSFORM_NPC,
|
||||
|
||||
// NPC 행동 업데이트
|
||||
ACTION_NPC,
|
||||
|
||||
// NPC 스테이트 업데이트
|
||||
STATE_NPC,
|
||||
|
||||
// 데미지 UI 전달
|
||||
DAMAGE
|
||||
}
|
||||
|
||||
public class PacketHeader
|
||||
{
|
||||
public PacketCode Code;
|
||||
public int BodyLength;
|
||||
}
|
||||
28
ClientTester/EchoClientTester/Program.cs
Normal file
28
ClientTester/EchoClientTester/Program.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using ClientTester.EchoDummyService;
|
||||
using Serilog;
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
|
||||
const string SERVER_IP = "localhost";
|
||||
const int SERVER_PORT = 9500;
|
||||
const string CONNECTION_KEY = "game";
|
||||
const int CLIENT_COUNT = 1000;
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
Console.CancelKeyPress += (_, e) =>
|
||||
{
|
||||
e.Cancel = true;
|
||||
Log.Warning("[SHUTDOWN] Ctrl+C 감지, 종료 중...");
|
||||
cts.Cancel();
|
||||
};
|
||||
|
||||
var service = new DummyClientService(CLIENT_COUNT, SERVER_IP, SERVER_PORT, CONNECTION_KEY, 100);
|
||||
|
||||
await service.RunAsync(cts.Token);
|
||||
|
||||
service.PrintStats();
|
||||
service.Stop();
|
||||
|
||||
Log.CloseAndFlush();
|
||||
Reference in New Issue
Block a user