Your SlideShare is downloading. ×
0
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
C# Game Server
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

C# Game Server

2,835

Published on

implements game server using c# features such as async, await, dynamic, linq, reflection, attribute, ienumerable.

implements game server using c# features such as async, await, dynamic, linq, reflection, attribute, ienumerable.

Published in: Engineering
0 Comments
9 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,835
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
32
Comments
0
Likes
9
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. C# Server 만들기 2013. 06. 07. 최재영
  • 2. Why? • 빌드 속도 • 표현력 2 async await extension method linq Observable TPL DynamicObject Reflection Attribute IEnumerable
  • 3. 흐름 3 Network Datasheet Database Logic async, await TaskCompletionSource Reflection Attribute Dynamic XmlLinq IEnumerable
  • 4. Network • 빠른 패킷 처리를 위해 비동기 IO 사용 4 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IPv4); // preprocess socket var buffer = new byte[4096]; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, result => { var received = socket.EndReceive(result); // process packet }, null /* state */);
  • 5. TaskCompletionSource • C++ Future + C# Task Awaitable Future 5 Network static Task<int> ReceiveAsync(this Socket sock, byte[] buf, int off, int size) { var source = new TaskCompletionSource<int>(sock); sock.BeginReceive(buf, off, size, SocketFlags.None, state => { try { source.SetResult(socket.EndReceive(state)); } catch (Exception e) { source.SetException(e); } }, source); return source.Task; }
  • 6. async, await • Task.Result 비동기 대기(await), 그런 코드가 있는 함수(async) 6 Network static async Task<byte[]> ReceiveAsync(this Socket socket, int count) { var buffer = new byte[count]; var length = 0; do { var num = await ReceiveAsync(socket, buffer, length, count); if (num == 0) break; length += num; count -= num; } while (count > 0); if (length != buffer.Length) throw new IOException("packet is truncated."); return buffer; }
  • 7. async, await 7 Network async void ReceiveLoop(Socket socket) { while (true) { var lengthBytes = await socket.ReceiveAsync(sizeof (int)); var packetBytes = await socket.ReceiveAsync( BitConverter.ToInt32(lengthBytes, 0)); // process packet var packet = ReadPacket(packetBytes); _handlerMap[packet.GetType()](packet); } } await하는 지점에 아직 IO signal이 없다면, 해당 Task는 잠시 멈추고, 가용한 다른 Task를 찾아 수행함
  • 8. Listener (Server) • ClientSocket을 비동기로 Accept해서, • 각 Socket마다 비동기로 Packet을 대기해서 처리함 8 Network var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var localEndPoint = new IPEndPoint(IPAddress.Any, Port); listener.Bind(localEndPoint); listener.Listen(100); while (true) { var clientSocket = await listener.AcceptAsync(); ReceiveLoop(clientSocket); } async method
  • 9. Summary • Socket의 Async 계열 함수를 사용 (.NET 내부는 IOCP로 처리) • async, await은 TaskContinuation의 Syntax sugar • async, await Keyword로 Callback 없이 편하게 Network 코드 작성 • 그러면서도 Thread Pool에 의한 효율적으로 수행됨 (.NET Thread Pool도 내부에서 IOCP로 관리) 9 Network
  • 10. Datasheet • dynamic을 사용한 일반적인 Xml 읽기 • code generator를 사용 10 Xml 작성 Xml Model 구현 Xml Parser 구현 자동 생성 자동 생성 code generator DynamicObject XmlDefinition (자동 생성) general-loader
  • 11. DynamicObject • RuntimeType으로 동적으로 멤버 접근 가능 • 올바른 Type의 값을 미리 준비해야 하므로 XmlDefinition 필요 11 Datasheet public class XmlObject : DynamicObject { private readonly Dictionary<string, object> _attributes; private readonly Dictionary<string, IEnumerable<XmlObject>> _multipleChildren; private readonly Dictionary<string, XmlObject> _singleChildren; public override bool TryGetMember(GetMemberBinder binder, out object result) { return TryGetValue(_attributes, binder.Name, out result) || TryGetValue(_singleChildren, binder.Name, out result) || TryGetValue(_multipleChildren, binder.Name, out result); }
  • 12. DynamicObject • Attribute를 읽을 때 Type 변환을 미리 수행 • dynamic으로 접근하여 Model 없이 접근 가능 12 Datasheet _attributes = element.Attributes.OfType<XmlAttribute>() .ToDictionary(e => e.Name, e => defNode.SelectAttribute(e.Name) .ReadValue(e.Value)); <?xml version="1.0" encoding="utf-8" ?> <World> <Config port="40123"/> </World> World.Config@port : int dynamic world = XmlObject.Load("World.xml", _def); _listener.Port = world.Config.port;
  • 13. Summary • dynamic을 사용하여 코딩 시간 단축(Model, Parser 작성 불필요) • 오타로 인한 접근 위반은 Runtime에 확인 가능 • XmlDefinition이 필요함(자동 생성 가능) • 보다 빠른 속도를 원할 경우에는 Model, Parser를 Generate (IVsSingleFileGenerator) 13 Datasheet
  • 14. Database • Reflection과 Attribute 사용으로 일반적인 Bind 구현 • scheme 작업이 불필요할 경우 model 작성만으로 모든 구현 해결 가능 14 Scheme 작성 DataModel 작성 Bind 구현 nosql or generator Reflection
  • 15. Reflection • Runtime에 model의 type정보로 scheme를 구축 • 각 데이터의 Serialize/Deserialize 구현 필요 (String과 object 상호 변환) • 모든 Model 객체를 Xml로 변환 15 Database new XElement("Objects", _gameObjects.Values.Select( obj => new XElement("Object", obj.GetType().GetProperties() .Where(e => e.CanRead && e.CanWrite) .Select(e => new XAttribute(e.Name, SerializeValue(e.PropertyType, e.GetValue(obj, null)) ))))); 모든 Property에 대해 출력 출력할 때에는 string으로, 읽을 때에는 다시 object로
  • 16. Attribute • Runtime에 접근 가능한 metadata를 코드에 주입 16 Database [CommandHandler("npc", "새로운 Npc를 생성합니다")] internal bool SpawnNpc(Entity admin, [CommandArgument("Npc의 이름")] string npcName, [CommandArgument("Npc의 X 위치", 0)] double newX, [CommandArgument("Npc의 Y 위치", 0)] double newY) { if (!admin.Has<Pos>()) return false; var npc = EntityManager.Instance.Create(EntityTemplate.Ids.Npc); npc.Get<Motion>().Dir = admin.Get<Motion>().Dir; npc.Get<Nameplate>().Name = npcName; npc.Get<Pos>().Assign(new Pos {X = newX, Y = newY}); 명령어와 설명을 코드에 기록 인자 설명과 기본 값, type을 코드에 기록
  • 17. Summary • model 객체의 type 정보를 최대한 사용 • Attribute를 부여하여 가능한 많은 정보를 코드에 주입 (DSL, 문서, 주석 등 외부 정보는 추가 유지 보수가 필요함) • Dirty나 Lazy를 사용하여 최적화 가능 • 역시 보다 빠른 속도를 원할 경우에는 Code Generate를 사용 (partial class를 사용하여 사용자 코드와 혼합 가능) 17 Database
  • 18. Logic • yield return을 사용하여 State Machine 제거 • context 유지를 위한 별도 코딩이 필요 없음 18 행동 A 행동 B 행동 C 1초 뒤 3초 뒤 5초 뒤 Logic Engine Logic #1 Logic #2
  • 19. IEnumerable (Coroutine) • IEnumerable을 반환 type으로 설정하여 yield return 사용 • 다음 로직 수행까지의 대기 시간을 반환 19 Logic public IEnumerable<int> RegenerateEntry() { while (true) { var newNpc = _context.NewGameObject(ObjectType.Npc); _context.AddGameObject(newNpc); _context.BroadcastPacket(newNpc.ToSpawnPacket()); var newAi = new EachAi(this, newNpc); _context.AddEntry(newAi.AiLogicEntry); var nextInterval = _random.Next(interval) + base; yield return nextInterval; } 제어권이 호출자에게 넘어감 다음 호출 시 이 지점부터 수행
  • 20. IEnumerator (Coroutine) • IEnumerator의 MoveNext() 함수로 코드 실행 • Current로 yield return 결과 값 확인 20 Logic var enumerator = RegenerateEntry().GetEnumerator(); while (enumerator.MoveNext()) { Thread.Sleep(enumerator.Current); } IEnumerable로부터 IEnumerator를 가져옴 MoveNext()로 yield 사이 구간 코드 수행 yield return 반환 값을 얻음. 대기 시간만큼 쉼 여러 IEnumerator를 관리하고 Thread.Sleep()을 보다 작은 단위로 수행
  • 21. LogicEngine (Coroutine) 21 Logic public void EntryLoop() { var prev = DateTime.Now; while (true) { var now = DateTime.Now; var delta = (now - prev).Milliseconds; foreach (var newOne in _newLogicEntries) { var newEntry = new LogicEntry { Enumerator = newOne().GetEnumerator(), SleepTime = 0 }; _logicEntries.Add(newEntry); } _newLogicEntries.Clear(); 새로 추가된 Entry로부터 IEnumerator객체를 생성
  • 22. LogicEngine (Coroutine) 22 Logic var removals = new List<LogicEntry>(); foreach (var each in _logicEntries) { each.SleepTime -= delta; if (each.SleepTime >= 0) continue; if (!each.Enumerator.MoveNext()) removals.Add(each); else each.SleepTime = each.Enumerator.Current; } _logicEntries.RemoveAll(removals.Contains); prev = now; const int logicInterval = 16; Thread.Sleep(logicInterval); } } 수행 가능한 IEnumerator 집합 수행할 시간이 된 로직을 찾아서 실행 로직 함수가 return되어 완료되면 MoveNext()가 fasle를 반환 수행이 완료된 로직 삭제
  • 23. Summary • IEnumerable과 yield return의 조합으로 coroutine 구현 • 간단한 coroutine이지만 많은 boiler plate 코드 작성 회피 가능 • yield return으로 많은 정보를 전달하여 다양한 활용 (Unity3D Engine) • 여러 Thread가 LogicEngine을 수행하여 Entry 수행 분산 가능 (여러 Thread가 수행할 경우 Lfe 등 객체별 수행 동기화 고려가 필요함) • Script를 C#으로 작성 시 도움이 될 듯(?) (c# script + roslyn + linqpad + nuget) 23 Logic
  • 24. Summary • async, await을 사용한 동기적 Network(IO) 프로그래밍 • dynamic을 사용한 Runtime type dispatch • Reflection, Attribute를 사용한 boiler plate 코드 줄이기 • Attribute로 metadata를 코드로 기록하여 유지 보수 비용 줄이기 • coroutine을 사용한 동기적 로직 프로그래밍 24 보다 적은, 그리고 직관적인(동기적) 코딩으로 유지 보수 비용 줄이기 높은 표현력 성능 문제는 회로의 발전이 해결해 줄 것입니다 [...]
  • 25. How much faster is C++ than C#? 25 C# may not be faster, but it makes YOU/ME faster. That's the most important measure for what I do. :) http://stackoverflow.com/questions/138361/how-much-faster-is-c-than-c

×